#install.packages("FNN")
#install.packages("ggrepel")
#install.packages("yardstick")
#install.packages("igraph")
#install.packages("caret")
#update.packages("caret")
#install.packages("reactable")

library(tidyverse)
library(sf)
library(raster)
library(knitr)
library(kableExtra)
library(tidycensus)
library(tigris)
library(FNN)
library(caret)
library(yardstick)
library(pscl)
library(plotROC) 
library(ggrepel)
library(pROC)
library(grid)
library(gridExtra)
library(viridis)
library(igraph)
library(maptools)
library(reactable)
library(classInt)

plotTheme <- theme(
  plot.title =element_text(size=12),
  plot.subtitle = element_text(size=8),
  plot.caption = element_text(size = 6),
  axis.text.x = element_text(size = 10, angle = 45, hjust = 1),
  axis.text.y = element_text(size = 10),
  axis.title.y = element_text(size = 10),
  # Set the entire chart region to blank
  panel.background=element_blank(),
  plot.background=element_blank(),
  #panel.border=element_rect(colour="#F0F0F0"),
  # Format the grid
  panel.grid.major=element_line(colour="#D0D0D0",size=.75),
  axis.ticks=element_blank())

mapTheme <- theme(plot.title =element_text(size=12),
                  plot.subtitle = element_text(size=8),
                  plot.caption = element_text(size = 6),
                  axis.line=element_blank(),
                  axis.text.x=element_blank(),
                  axis.text.y=element_blank(),
                  axis.ticks=element_blank(),
                  axis.title.x=element_blank(),
                  axis.title.y=element_blank(),
                  panel.background=element_blank(),
                  panel.border=element_blank(),
                  panel.grid.major=element_line(colour = 'transparent'),
                  panel.grid.minor=element_blank(),
                  legend.direction = "vertical", 
                  legend.position = "right",
                  plot.margin = margin(1, 1, 1, 1, 'cm'),
                  legend.key.height = unit(1, "cm"), legend.key.width = unit(0.2, "cm"))

#Colors: Viridis Plasma (option "C")
gray <- "#D3D3D3"
darkGray <- "#505251"
highlight <- "#0D0887FF"
palette2 <- c("#FA9E3BFF", "#0D0887FF")
palette3 <- c("#FDC926FF", "#D8576BFF", "#0D0887FF")
palette4 <- c("#FA9E3BFF", "#D8576BFF", "#9C179EFF", "#0D0887FF")
palette5 <- c("#FDC926FF", "#ED7953FF", "#D8576BFF", "#9C179EFF", "#0D0887FF")
palette10 <- c("#F0F921FF", "#FDC926FF", "#FA9E3BFF", "#ED7953FF", "#D8576BFF",
               "#BD3786FF", "#9C179EFF", "#7301A8FF", "#47039FFF", "#0D0887FF")

#R Markdown Cheat sheet for reference - https://www.rstudio.com/wp-content/uploads/2015/02/rmarkdown-cheatsheet.pdf
#this function converts a column in to quintiles. It is used for mapping.
quintileBreaks <- function(df,variable) {
    as.character(quantile(df[[variable]],
                          c(.01,.2,.4,.6,.8),na.rm=T))
}

#This function can be used to convert a polygon sf to centroids xy coords.
xyC <- function(aPolygonSF) {
  as.data.frame(
    cbind(x=st_coordinates(st_centroid(aPolygonSF))[,1],
          y=st_coordinates(st_centroid(aPolygonSF))[,2]))
} 

#this function convert a raster to a data frame so it can be plotted in ggplot
rast <- function(inRaster) {
  data.frame(
    xyFromCell(inRaster, 1:ncell(inRaster)), 
    value = getValues(inRaster)) }

#this function aggregates rasters
aggregateRaster <- function(inputRasterList, AtlantaMSA_fishnet) {
  #create an empty fishnet with the same dimensions as the input fishnet
  theseFishnets <- AtlantaMSA_fishnet %>% dplyr::select()
  #for each raster in the raster list
  for (i in inputRasterList) {
  #create a variable name corresponding to the ith raster
  varName <- names(i)
  #convert raster to points as an sf
    thesePoints <-
      rasterToPoints(i) %>%
      as.data.frame() %>%
      st_as_sf(coords = c("x", "y"), crs = st_crs(AtlantaMSA_fishnet)) %>%
      filter(.[[1]] == 1)
  #aggregate to the fishnet
    thisFishnet <-
      aggregate(thesePoints, AtlantaMSA_fishnet, length) %>%
      mutate(!!varName := ifelse(is.na(.[[1]]),0,1))
  #add to the larger fishnet
    theseFishnets <- cbind(theseFishnets, thisFishnet)
  }
  #output all aggregates as one large fishnet
   return(theseFishnets)
}

Introduction: This Model and Its Significance to Planners

Welcome! In this interactive R-markdown memo, we employ historical patterns of development and population trends in the Atlanta, Georgia metropolitan statistical area (“MSA”) to predict future development scenarios. Specifically, we examine the spatial relationships that exist among population growth and highway infrastructure with land development. Through these models, we explore the relationships between each of these independent variables and urban sprawl, our dependent variable.

Urban sprawl is a development scenario characterized by “leap frogging” development, a development scenario that disproportionately consumes land and environmental resources, with significant negative ecological externalities. These models will predict likely urban sprawl for the Atlanta metro area based on two scenarios. The models presented here enable planners and policy makers to preemptively develop policies to calibrate against urban sprawl, empowering municipal governments to create conscientious land allocation plans.

The model developed will predict two different scenarios: demand-side and supply-side. In the demand-side model, we attempt to understand the relationship that population growth has with urban sprawl in the MSA. Specifically, we predict the demand for development according to population growth, and how this would spatially manifest based on existing land use trends and population projections. The supply-side model will predict how future development might occur following the construction of a new, theoretical highway, supplying infrastructure. Through this scenario, we attempt to ascertain the relationship that highway development has with urban sprawl.

Let’s explore how sprawl can occur, unmitigated, based on these independent variables. We use land cover and population data from the years 2008/2009 and 2019 to predict for the year 2029.

  • Region of interest: Atlanta, GA metro

  • Years of study: 2008-2019

  • Projecting to 2029

  • Data level: fishnet grid cell

The following chunks of code will collect the prerequisite data needed for the model from the two years of study, structure the data in preparation for modeling, and run the demand - and supply- side models based on the study years’ data to project for the year 2030.

Data Gathering and Feature Engineering

First, we pull in and visualize our spatial datasets of the Atlanta MSA’s 21 counties.

And retrieve the Atlanta MSA boundary using the tigris package.

#List of the Atlanta region MPOs 21 counties
Counties <- c("Barrow", "Bartow", "Carroll", "Cherokee", 
              "Clayton", "Cobb", "Coweta", "Dawson", 
              "DeKalb", "Douglas", "Fayette", "Forsyth", 
              "Fulton", "Gwinnett", "Hall", "Henry", 
              "Newton", "Paulding", "Rockdale", "Spalding", "Walton")

#First pull all MSAs in the U.S., then search for yours and filter with the correct CBSAFP code
metro_counties <- counties(state = "GA", cb = TRUE, resolution = "500k", year = NULL) %>%
  filter(NAME %in% Counties) %>% 
  st_transform("ESRI:102267")

#Merge counties into one shapefile showing just the boundary
Atlanta_MSA <- st_union(metro_counties) %>% 
  st_transform("ESRI:102267")
#plot MSA boundary to make sure its right
ggplot() + 
  geom_sf(data = metro_counties) +
  geom_sf(data = Atlanta_MSA, 
          color = "black", fill = "transparent", linewidth = .75) +
  mapTheme + 
  labs(title = "Atlanta 21 County MSA")

We then create a fishnet of the Atlanta MSA using 1000’ x 1000’ cells. A fishnet is a vector data set that will allow us to attach other information to the forthcoming raster data set on land cover.

AtlantaMSA_fishnet <- 
    st_make_grid(metro_counties, cellsize = 1000, square = TRUE) %>%
  st_sf() %>% 
  st_transform("ESRI:102267")

AtlantaMSA_fishnet <-
  AtlantaMSA_fishnet[metro_counties,]

#plot MSA boundary to make sure its right
ggplot()+
  geom_sf(data = AtlantaMSA_fishnet,
          fill = "lightgrey") +
  geom_sf(data = Atlanta_MSA, 
          color = "black", fill = "transparent", linewidth = .75) +
  labs(title = "Atlanta MSA Fishnet") +
  mapTheme

Land Cover

Land Cover by Year and Type

We retrieve the raster files of land cover for the Atlanta MSA. We downloaded these datasets from the Multi-Resolution Land Characteristics Consortium’s National Land Cover Database, and clipped them in ArcGIS to a rectangular boundary extent that aligned with the farthest points of the Atlanta region’s 21 county border.

##SKIP##
#load full size rasters
lc_2008 = raster("https://raw.githubusercontent.com/c-townsley/LUEM_A5_Repo/56bd35b343d39e88bd8e1fdc89957e6ccb804fe9/Data/raster/2008LC_proj.tif")
    
lc_2019 = raster("https://raw.githubusercontent.com/c-townsley/LUEM_A5_Repo/56bd35b343d39e88bd8e1fdc89957e6ccb804fe9/Data/raster/2019LC_proj.tif")

The raster files were still very large, so we down-sampled them to make their size more manageable and then clipped them precisely to the MSA boundary.

## SKIP ##

#down sample the raster to make more manageable
lc_2008 <- aggregate(lc_2008, fact = 7, fun = modal)
lc_2019 <- aggregate(lc_2019, fact = 7, fun = modal)

#crop rasters
lc_2008_crop <- crop(lc_2008, extent(metro_counties))
lc_2019_crop <- crop(lc_2019, extent(metro_counties)) 

#mask rasters
lc_2008_crop <- mask(x = lc_2008_crop, mask = metro_counties)
lc_2019_crop <- mask(x = lc_2019_crop, mask = metro_counties)

#Export downsized rasters
#writeRaster(lc_2008_crop, filename=file.path(rast_dir, "lc2008_sml.tif"), format="GTiff", overwrite=TRUE)

#writeRaster(lc_2019_crop, filename=file.path("C:/Users/ctown/OneDrive - PennO365/Classes/Classes_Sem4_2023Spring/CPLN 675_Land Modeling/Assignments/A5_SprawlForecasting/LUEM_A5_Repo/Data/raster", "lc2019_sml.tif"), format="GTiff", overwrite=TRUE)
lc_2008 <- raster("https://raw.githubusercontent.com/c-townsley/LUEM_A5_Repo/24f08e61c5ec983b3fa39f1f3b03fb90aa3a13ee/Data/raster/lc2008_sml.tif") 

lc_2019 <- raster("https://raw.githubusercontent.com/c-townsley/LUEM_A5_Repo/24f08e61c5ec983b3fa39f1f3b03fb90aa3a13ee/Data/raster/lc2019_sml.tif") 

This allows us to visualize Atlanta’s land cover in 2008 and 2019.

grid.arrange(ncol = 1,
ggplot() +
  geom_raster(data=rast(lc_2008) %>% na.omit %>% filter(value > 0), 
              aes(x,y,fill=as.factor(value))) +
  scale_fill_viridis(discrete=TRUE,
                     name ="",
                     option = "C") +
  geom_sf(data=metro_counties,
          color = "white",
          fill = "transparent",
          linewidth = .5) +
    geom_sf(data=Atlanta_MSA,
          color = "black",
          fill = "transparent",
          linewidth = .75) +
  labs(title = "Land Cover, 2008") +
  mapTheme +
  theme(legend.direction="horizontal"),


ggplot() +
  geom_raster(data=rast(lc_2019) %>% na.omit %>% filter(value > 0), 
              aes(x, y, fill=as.factor(value))) +
  scale_fill_viridis(discrete=TRUE,
                     name ="",
                     option = "C") +
  geom_sf(data=metro_counties,
          color = "white",
          fill = "transparent",
          linewidth = .5) +
      geom_sf(data=Atlanta_MSA,
          color = "black",
          fill = "transparent",
          linewidth = .75) +
  labs(title = "Land Cover, 2019") +
  mapTheme +
  theme(legend.direction="horizontal")
)

Viewing land cover at the MSA level in 2008 and 2019 shows that change is happening at too small a scale to discern visually at this extent. Therefore, we summon the powers of R. First, we distill the many specific land cover classifications into six simple buckets: developed land, forest, farmland, wetlands, water bodies, and other undeveloped lands.

Land Cover by Type

developed08 <- lc_2008 == 21 | lc_2008 == 22 | lc_2008 == 23 | lc_2008 == 24
forest08 <- lc_2008 == 41 | lc_2008 == 42 | lc_2008 == 43 
farm08 <- lc_2008 == 81 | lc_2008 == 82 
wetlands08 <- lc_2008 == 90 | lc_2008 == 95 
otherUndeveloped08 <- lc_2008 == 52 | lc_2008 == 71 | lc_2008 == 31 
water08 <- lc_2008 == 11

developed19 <- lc_2019 == 21 | lc_2019 == 22 | lc_2019 == 23 | lc_2019 == 24
forest19 <- lc_2019 == 41 | lc_2019 == 42 | lc_2019 == 43 
farm19 <- lc_2019 == 81 | lc_2019 == 82 
wetlands19 <- lc_2019 == 90 | lc_2019 == 95 
otherUndeveloped19 <- lc_2019 == 52 | lc_2019 == 71 | lc_2019 == 31 
water19 <- lc_2019 == 11

names(developed08) <- "developed08"
names(forest08) <- "forest08"
names(farm08) <- "farm08"
names(wetlands08) <- "wetlands08"
names(otherUndeveloped08) <- "otherUndeveloped08"
names(water08) <- "water08"

names(developed19) <- "developed19"
names(forest19) <- "forest19"
names(farm19) <- "farm19"
names(wetlands19) <- "wetlands19"
names(otherUndeveloped19) <- "otherUndeveloped19"
names(water19) <- "water19"

This distilled, categorical dataset for each year is then aggregated to our fishnet of the Atlanta MSA. The resulting maps show that Atlanta is a heavily forested area. Development is vast, with its anchor in the center of the MSA, radiating outward. A significant amount of farmland exists in a thick band against the MSA’s periphery. Undeveloped areas are scattered, also mostly near the MSA’s periphery.

theRasterList08 <- c(developed08, forest08, farm08, wetlands08, otherUndeveloped08, water08)

theRasterList19 <- c(developed19, forest19, farm19, wetlands19, otherUndeveloped19, water19)

aggregatedRasters08 <-
  aggregateRaster(theRasterList08, AtlantaMSA_fishnet) %>%
  dplyr::select(developed08, forest08, farm08, wetlands08, otherUndeveloped08, water08) %>%
  mutate_if(is.numeric, as.factor)

aggregatedRasters19 <-
  aggregateRaster(theRasterList19, AtlantaMSA_fishnet) %>%
  dplyr::select(developed19, forest19, farm19, wetlands19, otherUndeveloped19, water19) %>%
  mutate_if(is.numeric, as.factor)

rasters08_map <- aggregatedRasters08 %>%
  gather(var,value,developed08:water08) %>%
  st_cast("POLYGON") %>%    #just to make sure no weird geometries slipped in
  mutate(X = xyC(.)$x,
         Y = xyC(.)$y) %>%
  ggplot() +
    geom_sf(data=metro_counties) +
    geom_point(aes(X,Y, colour=as.factor(value))) +
    facet_wrap(~var) +
    scale_colour_manual(values = c(gray, highlight),
                        labels=c("False","True"),
                        name = "") +
    geom_sf(data=metro_counties,
          color = "white",
          fill = "transparent",
          linewidth = .25) +
    geom_sf(data=Atlanta_MSA,
          color = "black",
          fill = "transparent",
          linewidth = .5) +
    labs(title = "Land Cover Types, 2008",
         subtitle = "As fishnet centroids") +
   mapTheme +
   theme()  +
   theme(legend.position = "none")

rasters08_map

Land Cover Change

To identify areas with land cover change, we use the raster data sets and reclassify the land cover types as numerical values, where developed lands have the value of 1 and undeveloped lands have the value of 0, for each of the years’ data sets. We are then able to add these data sets together on a cell-by-cell basis to understand where development has occurred in 2019 and where it was not in 2008. When added together, the cells with values of 1 represent where new development has occurred; a value of 2 shows where development was in 2008 and remained in 2019, and a value of 0 shows land that was and remains undeveloped. This function is like using raster calculator in GIS.

#creat matrix for reclassifying land cover
reclassMatrix <- 
  matrix(c(
    0,12,0,
    12,24,1,
    24,Inf,0),
  ncol=3, byrow=T)

reclassMatrix
#reclassify land cover change using reclass matrix

lc_2008r <- 
  reclassify(lc_2008, reclassMatrix)
lc_2019r <- 
  reclassify(lc_2019, reclassMatrix)

lc_2008r[lc_2008r < 1] <- 0
names(lc_2008r) <- "dev08"

lc_2019r[lc_2019r < 1] <- 0
names(lc_2019r) <- "dev19"

development_change <- lc_2008r + lc_2019r

# convert the raster to a vector
dev_change_vec <- as.vector(development_change)

# create the histogram plot with ggplot2
ggplot(data.frame(value = dev_change_vec), aes(x=value)) + 
  geom_histogram(binwidth=.5, fill=highlight, color="white") +
  labs(title="Development Change by Type (2008-2019)",
       subtitle = "0 = Remained Undeveloped, 1 = New Development, 2 = Already Developed",
       x="Development Change",
       y="Frequency") +
  plotTheme

This shows us that most cells in the MSA remained undeveloped from 2008 to 2019, many of them were already developed, and a few of the previously undeveloped cells converted to developed. We can then visualize where the new development cells with the 1 value are, which shows that new development has been scattered throughout the MSA.

development_change[development_change != 1] <- NA

ggplot() +
  geom_sf(data=Atlanta_MSA) +
  geom_raster(data=rast(development_change) %>% na.omit, 
              aes(x,y,fill=as.factor(value))) +
  scale_fill_viridis(discrete=TRUE,
                     option = "C",
                     name ="Cells \nwith Land Cover\nChange") + 
  geom_sf(data=metro_counties,
          color = "white",
          fill = "transparent",
          linewidth = .5) +
  geom_sf(data=Atlanta_MSA,
          color = "black",
          fill = "transparent",
          linewidth = .75) +
  labs(title="Development Land Cover Change (2008-2019)",
       subtitle = "Atlanta metro area | Raster cells") +
  mapTheme +
   theme(legend.position = "none")

We then join this dataset, mapping new development, to our fishnet. This has the effect of magnifying where the development change has happened a bit.

dev_change <- reclassify(development_change, reclassMatrix)
                         
dev_change[development_change < 1] <- NA

names(dev_change) <- "development_change"


changePoints <-
  rasterToPoints(dev_change) %>%
  as.data.frame() %>%
  st_as_sf(coords = c("x", "y"), crs = st_crs(AtlantaMSA_fishnet))

dev_change_fishnet <- 
  aggregate(changePoints, AtlantaMSA_fishnet, sum) %>%
  mutate(development_change = ifelse(is.na(development_change),0,1),
         development_change = as.factor(development_change))

ggplot() +
  geom_point(data=dev_change_fishnet, 
             aes(x=xyC(dev_change_fishnet)$x, y=xyC(dev_change_fishnet)$y, colour=development_change)) +
  scale_colour_manual(values = c(gray, highlight),
                      labels=c("No Change","New Development"),
                      name = "") +
    geom_sf(data=metro_counties,
          color = "white",
          fill = "transparent",
          linewidth = .5) +
    geom_sf(data=Atlanta_MSA,
          color = "black",
          fill = "transparent",
          linewidth = .75) +
  labs(title = "New Development (2019)", subtitle = "Atlanta metro area | Fishnet centroids") +
  mapTheme

#dev_change_fishnet <- st_write(dev_change_fishnet, "C:/Users/Lindsey/Desktop/CPLN675_Land_Use_Modeling_Desktop/Final/LUEM_A5_Repo/Data/geojson/dev_change_fishnet.geojson")

Census Data: Population

Next, we set up our MSA population data, which will prepare us for the demand-side model. The next two chunks download the population data for 2009 and 2019 from the American Community Survey 5-year Estimates on a census tract level.

We then visualize the population for the two years by the respective census tract boundaries for each of the years.

grid.arrange(
ggplot() +
  geom_sf(data = Pop09, aes(fill=factor(ntile(pop09,5))), colour=NA) +
  geom_sf(data = Atlanta_MSA, 
          color = "black", fill = "transparent", linewidth = .75) +
  scale_fill_manual(values = palette5,
                    labels=quintileBreaks(Pop09,"pop09"),
                   name="Quintile\nBreaks") +
  labs(title="2009 Population\nAtlanta 21-County MSA",
       subtitle="By Census Tract") +
  mapTheme,

ggplot() +
  geom_sf(data = Pop19, aes(fill=factor(ntile(pop19,5))), colour=NA) +
  scale_fill_manual(values = palette5,
                    labels=quintileBreaks(Pop19,"pop19"),
                    name="Quintile\nBreaks") +
  geom_sf(data = Atlanta_MSA, 
          color = "black", fill = "transparent", linewidth = .75) +
  labs(title="2019 Population\nAtlanta 21-County MSA",
       subtitle="By Census Tract") +
  mapTheme, ncol=2)

We then reconcile the tract boundaries with the fishnet grid cells to be able to conduct analysis and build models with our fishnet data set.

### Breaking here on select(fishnetID)
AtlantaMSA_fishnet <-
  AtlantaMSA_fishnet %>%
  rownames_to_column("fishnetID") %>% 
  mutate(fishnetID = as.numeric(fishnetID)) %>%
  dplyr::select(fishnetID)

fishnetPop09 <-
  st_interpolate_aw(Pop09["pop09"], AtlantaMSA_fishnet, extensive=TRUE) %>%
  as.data.frame(.) %>%
  rownames_to_column(var = "fishnetID") %>%
  left_join(AtlantaMSA_fishnet %>%
              mutate(fishnetID = as.character(fishnetID)),
            ., by=c("fishnetID"='fishnetID')) %>% 
  mutate(pop09 = replace_na(pop09,0)) %>%
  dplyr::select(pop09)

fishnetPop19 <-
  st_interpolate_aw(Pop19["pop19"], AtlantaMSA_fishnet, extensive=TRUE) %>%
  as.data.frame(.) %>%
  rownames_to_column(var = "fishnetID") %>%
  left_join(AtlantaMSA_fishnet %>%
              mutate(fishnetID = as.character(fishnetID)),
            ., by=c("fishnetID"='fishnetID')) %>% 
  mutate(pop19 = replace_na(pop19,0)) %>%
  dplyr::select(pop19)

fishnetPop <- 
  cbind(fishnetPop09, fishnetPop19) %>%
  dplyr::select(pop09,pop19) %>%
  mutate(pop_Change = pop19 - pop09)

We can then compare population by tract vs population by grid cell.

grid.arrange(
ggplot() +
  geom_sf(data=Pop19, aes(fill=factor(ntile(pop19,5))),colour=NA) +
  scale_fill_manual(values = palette5,
                    labels=substr(quintileBreaks(Pop19,"pop19"),1,4),
                   name="Quintile\nBreaks") +
  geom_sf(data = Atlanta_MSA, 
          color = "black", fill = "transparent", linewidth = .75) +
  labs(title="2019 Population\nAtlanta 21-County MSA",
       subtitle="Represented as tracts; Boundaries omitted") +
  mapTheme,

ggplot() +
  geom_sf(data=fishnetPop, aes(fill=factor(ntile(pop19,5))), colour=NA) +
  scale_fill_manual(values = palette5,
                   labels=substr(quintileBreaks(fishnetPop,"pop19"),1,4),
                   name="Quintile\nBreaks") +
  geom_sf(data = Atlanta_MSA, 
          color = "black", fill = "transparent", linewidth = .75) +
  labs(title="2019 Population\nAtlanta 21-County MSA",
       subtitle="Represented as fishnet gridcells; Boundaries omitted") +
  mapTheme, ncol=2)

Highway Distance

Next, we prepare our supply-side model by loading in spatial data for Atlanta’s existing highways.

## Reading layer `Major_Roads' from data source 
##   `https://raw.githubusercontent.com/c-townsley/LUEM_A5_Repo/8b689651286bb4ff28554095bd39ce1bd87655e4/Data/geojson/Major_Roads.geojson' 
##   using driver `GeoJSON'
## Simple feature collection with 2669 features and 6 fields
## Geometry type: LINESTRING
## Dimension:     XY
## Bounding box:  xmin: -85.35708 ymin: 32.84459 xmax: -83.50588 ymax: 34.57842
## Geodetic CRS:  WGS 84

This suggests there may be an association between proximity to highways and new development. To better understand this relationship, we calculate how far each fishnet cell is from a highway. We do this by converting the highway layer to raster, converting the raster to points, and then calculating the mean distance from a highway for each grid cell.

#SKIP#
#create a raster layer of highways
highway_raster <- 
  as(AtlantaHighways,'Spatial') %>%
  rasterize(.,emptyRaster)

#Export rasters

rast_dir = paste(dir_ct, 'raster', sep='/')

#writeRaster(highway_raster, filename=file.path(rast_dir, "highway_raster.tif"), format="GTiff", overwrite=TRUE)

This visualizes each cell’s distance from a highway.

Calculating the Spatial Lag of Development

We measure accessibility by way of a spatial lag, hypothesizing that new development is a function of distance to existing development. The shorter the distance, the more accessible a grid cell is to existing development. We measure this by calculating the average distance from each grid cell to its 2 nearest developed neighboring grid cells in 2009.

nn_function <- function(measureFrom,measureTo,k) {
  #convert the sf layers to matrices
  measureFrom_Matrix <-
    as.matrix(measureFrom)
  measureTo_Matrix <-
    as.matrix(measureTo)
  nn <-   
    get.knnx(measureTo, measureFrom, k)$nn.dist
    output <-
    as.data.frame(nn) %>%
    rownames_to_column(var = "thisPoint") %>%
    gather(points, point_distance, V1:ncol(.)) %>%
    arrange(as.numeric(thisPoint)) %>%
    group_by(thisPoint) %>%
    summarize(pointDistance = mean(point_distance)) %>%
    arrange(as.numeric(thisPoint)) %>% 
    dplyr::select(-thisPoint) %>%
    pull()
  
  return(output)  
}
AtlantaMSA_fishnet$lagDevelopment08 <-
    nn_function(xyC(AtlantaMSA_fishnet),
                xyC(filter(aggregatedRasters08, developed08==1)),
                2)

# compute natural breaks for "lagDevelopment08"
breaks <- classIntervals(AtlantaMSA_fishnet$lagDevelopment08, n=4, style="jenks")

#Plot
ggplot() +
  geom_sf(data=metro_counties) +
  geom_point(data=AtlantaMSA_fishnet, 
             aes(x=xyC(AtlantaMSA_fishnet)[,1], y=xyC(AtlantaMSA_fishnet)[,2], 
                 colour=cut(lagDevelopment08, breaks$brks)), size=1.5) +
  scale_colour_manual(values=palette5,
                      labels=format(breaks$brks, nsmall=1),
                      name="Distance (ft)") +
  geom_sf(data=metro_counties,
          color = "white",
          fill = "transparent",
          linewidth = .5) +
  geom_sf(data=Atlanta_MSA,
          color = "black",
          fill = "transparent",
          linewidth = .75) +
  labs(title = "Spatial Lag to 2008 Development",
       subtitle = "As fishnet centroids") +
  mapTheme

This shows most of the area in the MSA’s inner counties was near existing development in 2008. It was only the counties at the outer fringes that still had a few areas relatively distant from development.

Create Final Dataset

We then aggregate all these data to a final fishnet data set for feeding into our model. This fishnet includes columns which indicate, cell-by-cell, if there has been development change, land use type, the cell’s distance to highways, the cell’s distance to existing development, and its population in both years.

dat <-  
  cbind(
    AtlantaMSA_fishnet, dev_change_fishnet, highwayPoints_fishnet, fishnetPop, aggregatedRasters08) %>%
  dplyr::select(development_change, developed08, forest08, farm08, wetlands08, otherUndeveloped08, water08,
                pop09, pop19, pop_Change, distance_highways, lagDevelopment08) %>%
  st_join(metro_counties) %>%
  mutate(developed19 = ifelse(development_change  == 1 & developed08 == 1, 0, 1)) %>% #charlie to confirm if the else value should be 1 or 'developed'
  filter(water08 == 0) 

Data Exploration

Now that all of our data are assembled, we explore how each factor uncovered thus far is related to new development. This plot displays distance to highways and the spatial lag of development as continuous variables. This shows us that new development has been, on average, closer to highways, and closer to existing development in aggregate, than cells that had no change in land cover. This supports our previous hypothesis that new development might be occurring closer to highways and to previously developed areas.

dat %>%
  dplyr::select(distance_highways,lagDevelopment08,development_change) %>% 
  gather(Variable, Value, -development_change, -geometry) %>%
  ggplot(., aes(development_change, Value, fill=development_change)) + 
    geom_bar(position = "dodge", stat = "summary", fun.y = "mean") +
    facet_wrap(~Variable) +
    scale_fill_manual(values = palette2,
                      labels=c("No Change","New Development"),
                      name="") +
    labs(title="New Development as a Function of the Continuous Variables") +
    plotTheme

This next plot shows that new development has, on average, occurred in cells where the population is greater or growing. From this, we learn that development in Atlanta MSA is also positively correlated with the amount of population and population growth in an area.

dat %>%
  dplyr::select(pop09,pop19,pop_Change,development_change) %>%
  gather(Variable, Value, -development_change, -geometry) %>%
  ggplot(., aes(development_change, Value, fill=development_change)) + 
    geom_bar(position = "dodge", stat = "summary", fun.y = "mean") +
    facet_wrap(~Variable) +
    scale_fill_manual(values = palette2,
                      labels=c("No Change","New Development"),
                      name="") +
    labs(title="New Development as a Function of Factor Variables") +
    plotTheme

From the above data exploration, we can see that development is positively correlated with proximity to highways and development, as well as with existing and growing population. However, this leaves us wondering which types of land cover are most likely to convert to developed land?

The conversion table shows us that forested areas in 2008 were the most likely to have been developed by 2019. This shows that Atlanta has a preference for developing woodlands first, with areas adjacent to those that are already developed as a close second.

dat %>%
  dplyr::select(development_change:otherUndeveloped08,developed08) %>%
  gather(Land_Cover_Type, Value, -development_change, -geometry) %>%
   st_set_geometry(NULL) %>%
     group_by(development_change, Land_Cover_Type) %>%
     summarize(n = sum(as.numeric(Value))) %>%
     ungroup() %>%
    mutate(Conversion_Rate = paste0(round(100 * n/sum(n), 2), "%")) %>%
    filter(development_change == 1) %>%
  dplyr::select(Land_Cover_Type,Conversion_Rate) %>%
  reactable(theme = reactableTheme(style = list(fontFamily = "Arial, sans-serif", fontSize = "1.0rem")))


Binary Logistic Regression - Predicting for 2019

Now that we have gathered a number of factors we hypothesized to be related to new development, and have proved that they have at least some association with development, we can build and train a model to predict new development based on them.

We chose to use binomial logistic regression for this assignment because it is a type of statistical model that is well suited to analyzing relationships between a binary response variable and one or more predictor variables. It estimates the probability of the response variable taking one of two possible values (in this case developed or not developed) based on the values of the predictor variables.

To predict the likelihood of future development, we use data from 2008/2009 to create a series of logistic regression models to predict land use change in 2019. We then evaluate the performance of these models to select our best model for predicting development in 2029.

Model Building

The first step of this process is randomly splitting the data in half to create a testing data set with one half and a training data set with the other.

set.seed(3456)
trainIndex <- 
  createDataPartition(dat$developed08, p = .50,
                                  list = FALSE,
                                  times = 1)
datTrain <- dat[ trainIndex,]
datTest  <- dat[-trainIndex,]

We then build a series of binary logistic regression models based on our independent variables.

The six models we created are numbered to reflect increasing complexity. While Model 1 concentrates exclusively on land cover type and whether development change occurred between the two time periods, later models incorporate the spatial lag of development, population elements, and our distance variables.

Model1 <- glm(development_change ~ wetlands08 + forest08  + farm08 + otherUndeveloped08, 
              family="binomial"(link="logit"), data = datTrain)

Model2 <- glm(development_change ~ wetlands08 + forest08  + farm08 + otherUndeveloped08 + lagDevelopment08, 
              family="binomial"(link="logit"), data = datTrain)
              
Model3 <- glm(development_change ~ wetlands08 + forest08  + farm08 + otherUndeveloped08 + lagDevelopment08 + pop09, 
              family="binomial"(link="logit"), data = datTrain)          
              
Model4 <- glm(development_change ~ wetlands08 + forest08  + farm08 + otherUndeveloped08 + lagDevelopment08 + pop09 + pop19, 
              family="binomial"(link="logit"), data = datTrain)              
            
Model5 <- glm(development_change ~ wetlands08 + forest08  + farm08 + otherUndeveloped08 + lagDevelopment08 + pop09 + pop19 + pop_Change,
              family="binomial"(link="logit"), data = datTrain)              
              
Model6 <- glm(development_change ~ wetlands08 + forest08  + farm08 + otherUndeveloped08 + lagDevelopment08 + pop_Change + pop09 + pop19 + distance_highways, 
              family="binomial"(link="logit"), data = datTrain) 


modelList <- paste0("Model", 1:6)

map_dfc(modelList, function(x)pR2(get(x)))[4,] %>%
  setNames(paste0("Model",1:6)) %>%
  gather(Model,McFadden) %>%
  ggplot(aes(Model,McFadden)) +
    geom_bar(stat="identity", fill = highlight) +
    labs(title= "McFadden R-Squared by Model") +
    plotTheme
## fitting null model for pseudo-r2
## fitting null model for pseudo-r2
## fitting null model for pseudo-r2
## fitting null model for pseudo-r2
## fitting null model for pseudo-r2
## fitting null model for pseudo-r2

We then examine the McFadden R-Squared of the six models to select our best final model. Model 1 clearly performs more poorly than the others. However, models 2-4 also perform moderately less well than the remaining models. Therefore, we eliminate models 1-4 and choose Model 6 as our final model because it performs the best and allows us to explore the effects of all of our variables of interest.

We then use the predicted values from Model 6 to create a histogram of the model’s test set predicted probabilities. This shows us that a high density of cells have roughly a 40% likelihood of development. This makes sense given the sprawling nature of the Atlanta MSA’s existing development patterns, which are acreage intensive. It also makes sense given the map of new development we plotted earlier, in which it seems feasible that roughly /~30-40% of the MSA was covered by new development in 2019.

testSetProbs <- 
  data.frame(class = datTest$development_change,
             probs = predict(Model6, datTest, type="response")) 

chart_testProbs <-  
ggplot(testSetProbs, aes(probs)) +
  geom_density(aes(fill=class), alpha=0.75) +
  scale_fill_manual(values = palette2,
                    labels=c("No Change","New Development")) +
  labs(title = "Histogram of test set predicted probabilities",
       x="Predicted Probabilities",y="Density") +
  plotTheme

chart_testProbs

Accuracy

This next segment tests our model’s:

  1. Sensitivity: the extent to which it accurately predicts new development, or “true positives”, and:
  2. Specificity, the extent to which it accurately predicts “true negatives.”

Our ultimate goal with a land cover change model is to optimize the balance between the model’s predictions with regard to both sensitivity and specificity. Meaning, we want the model to be capable of predicting where development occurs so that we can plan for those areas, but we also want it to accurately predict where development would not occur, to inform our other land use planning.

At the first two thresholds specified below, 5% and 17%, we can see that the 5% threshold very accurately predicts the “true negatives,” where development does not occur. The threshold of 17%, however, performs better on the “true positive” rate of predicted v. actual development, that has occurred. We found, however, that a threshold of around 34% gives the best balance between the trade-offs of sensitivity and specificity. We chose 34% because it gives us the highest overall accuracy, and allows us to keep both our sensitivity and specificity over 50%.

options(yardstick.event_first = FALSE)

testSetProbs <- 
  testSetProbs %>% 
  mutate(predClass_05 = as.factor(ifelse(testSetProbs$probs >= 0.05 ,1,0)),
         predClass_17 = as.factor(ifelse(testSetProbs$probs >= 0.17 ,1,0)),
         predClass_34 = as.factor(ifelse(testSetProbs$probs >= 0.34 ,1,0))) 

testSetProbs %>%
  dplyr::select(-probs) %>%
  gather(Variable, Value, -class) %>%
  group_by(Variable) %>%
  summarize(Sensitivity = round(yardstick::sens_vec(class,factor(Value)),2),
            Specificity = round(yardstick::spec_vec(class,factor(Value)),2),
            Accuracy = round(yardstick::accuracy_vec(class,factor(Value)),2)) %>% 
  reactable(theme = reactableTheme(style = list(fontFamily = "Arial, sans-serif", fontSize = "1.0rem")))

We then convert these true/false positive/negative indicators to factors in order to map them.

predsForMap <-         
  dat %>%
    mutate(probs = predict(Model6, dat, type="response") ,
           Threshold_5_Pct = as.factor(ifelse(probs >= 0.05 ,1,0)),
           Threshold_17_Pct =  as.factor(ifelse(probs >= 0.17 ,1,0)),
           Threshold_34_Pct =  as.factor(ifelse(probs >= 0.34 ,1,0))) %>%
    dplyr::select(development_change,Threshold_5_Pct,Threshold_17_Pct, Threshold_34_Pct) %>%
    gather(Variable,Value, -geometry) %>%
    st_cast("POLYGON")

ggplot() +
  geom_point(data=predsForMap, aes(x=xyC(predsForMap)[,1], y=xyC(predsForMap)[,2], colour=Value)) +
  facet_wrap(~Variable) +
  scale_colour_manual(values = c(gray, highlight), labels=c("No Change","New Development"),
                      name="") +
  geom_sf(data=metro_counties,
          color = "white",
          fill = "transparent",
          linewidth = .5) +
  geom_sf(data=Atlanta_MSA,
          color = "black",
          fill = "transparent",
          linewidth = .75) +
  labs(title="Development Predictions - Low Threshold") + 
  mapTheme +
  theme(legend.position = "bottom")

These maps show that the 34% nicely captures the MSA’s overall pattern of development while minimizing the tendency to over-predict development that the 17% and 5% thresholds share (the 5% threshold wildly so).

To better understand the trade-offs between accurately predicting false positives and false negatives, we can run our predictions through a confusion matrix to visualize the true positives (“TrueP”) and true negatives (“TrueN”) in the charts below, for each of the thresholds.

dat <- dat %>% 
  mutate(probs = predict(Model6, dat, type="response"))
  
ConfusionMatrix.metrics <-
  dat %>%
    mutate(Threshold_5_Pct = as.factor(ifelse(probs >= 0.05 ,1,0)),
           Threshold_17_Pct =  as.factor(ifelse(probs >= 0.17 ,1,0)),
          Threshold_34_Pct =  as.factor(ifelse(probs >= 0.34 ,1,0))) %>%
    mutate(TrueP_05 = ifelse(development_change  == 1 & Threshold_5_Pct == 1, 1,0),
           TrueN_05 = ifelse(development_change  == 0 & Threshold_5_Pct == 0, 1,0),
           TrueP_17 = ifelse(development_change  == 1 & Threshold_17_Pct == 1, 1,0),
           TrueN_17 = ifelse(development_change  == 0 & Threshold_17_Pct == 0, 1,0),
           TrueP_34 = ifelse(development_change  == 1 & Threshold_34_Pct == 1, 1,0),
           TrueN_34 = ifelse(development_change  == 0 & Threshold_34_Pct == 0, 1,0)) %>%
    dplyr::select(., starts_with("True")) %>%
    gather(Variable, Value, -geometry) %>%
    st_cast("POLYGON") 

ggplot(data=ConfusionMatrix.metrics) +
  geom_point(aes(x=xyC(ConfusionMatrix.metrics)[,1], 
                 y=xyC(ConfusionMatrix.metrics)[,2], colour = as.factor(Value))) +
  facet_wrap(~Variable) +
  scale_colour_manual(values = c(gray, highlight),
                      labels=c("Incorrect","Correct"),
                      name="") +
  geom_sf(data=metro_counties,
          color = "white",
          fill = "transparent",
          linewidth = .5) +
  geom_sf(data=Atlanta_MSA,
          color = "black",
          fill = "transparent",
          linewidth = .75) +
  labs(title="Development Predictions - Low Threshold") +
  mapTheme +
  theme(legend.position = "bottom")

Mapping the sensitivity (true positives) and specificity (true negatives) of these thresholds shows in further detail that the 34% threshold predicts noticeably true negatives than the other thresholds while predicting only slightly less true positives. Therefore, we select 34% as the probability threshold at which to classify land as likely to be developed in the remainder of this project.

Planning Scenarios

Now that we have developed a predictive model for new development, we can use it to test the effects of potential future scenarios. We first use the model to predict how a demand-side change (projected population) might affect development in 2029. We then use the model to predict how a supply-side change (a new highway) might affect development

Scenario 1: Demand-side Change Forecast

In this demand-side projection, we will use population projections for the Atlanta MSA counties and distribute the population among our fishnet cells. The goal is to visualize change and plan for growth scenarios based on how anticipated future population might drive development.

First, we replace the lag development 2008 column with the distance from development in 2019.

dat <-
  dat %>%
  mutate(lagDevelopment08 = nn_function(xyC(.), xyC(filter(., developed19 == 1)),2))

Next, we look at projected population by county in 2029 based on population estimates from the Georgia Data Analytics Center.

#County projections 2029
#Barrow 96977 -
#Bartow 119594 - 
#Carroll 131577 -
#Cherokee  301752-
#Clayton  321410-
#Cobb  830671-
#Coweta  172341 -
#Dawson     32572-
#DeKalb  822154-
#Douglas  160635-
#Fayette  128627-
#Forsyth   316165-
#Fulton  1198334-
#Gwinnett   1044026-
#Hall       232720-
#Henry   278739-
#Newton 130559-
#Paulding 208196-
#Rockdale   95574-
#Spalding   70974-
#Walton     109205-

countyPopulation_2029 <- 
  data.frame(
   NAME = Counties,
   county_projection_2029 = 
     c(96977,119594,131577,301752,321410,830671,172341,32572,822154,160635,128627,
       316165,1198334,1044026,232720,278739,130559,208196,95574,70974,109205)) %>%
   left_join(
     dat %>%
       st_set_geometry(NULL) %>%
       group_by(NAME) %>%
       summarize(county_population_2019 = round(sum(pop19))))

countyPopulation_2029 %>%
  gather(Variable,Value, -NAME) %>%
  ggplot(aes(reorder(NAME,-Value),Value)) +
  geom_bar(aes(fill=Variable), stat = "identity", position = "dodge") +
  scale_fill_manual(values = palette2,
                    labels=c("2019","2029"),
                    name="Population") +
  labs(title="Population Change by County: 2019 - 2029",
       x="County", y="Population") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
  plotTheme

We see that Fulton, Gwinnett, DeKalb, and Cobb counties are projected to experience the largest population increases between 2019 and 2029.

Predicting Development Demand (based on projected population)

Now, we will take the county-level population projections and distribute them across the study area. We do this by weighting it proportionally according to a grid cell’s 2019 population and then subtracting 2019 pop to get pop_change.

dat_infill <-
  dat %>%
  #calculate population change
    left_join(countyPopulation_2029) %>%
    mutate(proportion_of_county_pop = pop19 / county_population_2019,
           pop_2029.infill = proportion_of_county_pop * county_projection_2029,
           pop_Change = round(pop_2029.infill - pop19),2) %>%
    dplyr::select(-county_projection_2029, -county_population_2019, 
                  -proportion_of_county_pop, -pop_2029.infill) %>%
  #predict for 2029
    mutate(predict_2029.infill = predict(Model6,. , type="response"))

map_pred29 <- 
dat_infill %>%
  ggplot() +  
  geom_point(aes(x=xyC(dat_infill)[,1], y=xyC(dat_infill)[,2], colour = factor(ntile(predict_2029.infill,5)))) +
  scale_colour_manual(values = palette5,
                    labels=substr(quintileBreaks(dat_infill,"predict_2029.infill"),1,4),
                    name="% Likelihood\nof Development") +
  geom_sf(data=metro_counties, fill=NA, colour="white", size=.75) +
  geom_sf(data = Atlanta_MSA, fill = "transparent",
          color = "black", linewidth = .75) +
  labs(title= "Development Demand in 2029: Predicted Probabilities",
    subtitle = "Based on population change projections")+
  mapTheme

map_pred29

Mapping predicted development based on projected population change for 2029 doesn’t show any huge changes from where our model predicted development would be in 2019. New development is still predicted to most likely occur in the outer counties. Although, our model is predicting increased development in the northwest and southwest corners of the metro which it was not predicting before we added in the 2029 population projections.


Scenario 2: Supply-side Change Forecast

To begin our supply-side projection, we imagine a new highway in Walton County, in the eastern part of the metro, where development between 2008 and 2019 was less concentrated. This was also a forested area, so the fact that much development had not happened there over the earlier period aroused our curiosity. This way, we can test the extent that a new road investment influences development in a desirable area of land cover. This new road was created as a line segment in ArcGIS and merged with the existing highways shapefile.

#NEW HIGHWAY SEGMENT ONLY
new_highway <- st_read("https://raw.githubusercontent.com/c-townsley/LUEM_A5_Repo/a5a5b0c9099b97f68c0d1aeadcdfe9dbd2960a9a/Data/geojson/new_highway.geojson")

new_highway <- new_highway %>% 
st_transform(st_crs(metro_counties)) %>%
  st_intersection(metro_counties) 

#new_highway <- st_write(new_highway,"C:/Users/Lindsey/Desktop/CPLN675_Land_Use_Modeling_Desktop/Final/LUEM_A5_Repo/Data/geojson/new_highway.geojson")

#ALL HIGHWAYS MERGED TOGETHER -- highways_all --  

highways_all <- st_read("https://raw.githubusercontent.com/c-townsley/LUEM_A5_Repo/a5a5b0c9099b97f68c0d1aeadcdfe9dbd2960a9a/Data/geojson/highways_all.geojson") %>% 
st_transform(st_crs(metro_counties)) %>%
  st_intersection(metro_counties)
ggplot() +
  geom_point(data=dev_change_fishnet, 
             aes(x=xyC(dev_change_fishnet)$x, y=xyC(dev_change_fishnet)$y, colour=development_change)) +
  scale_colour_manual(values = c(gray, "#ED7953FF"),
                      labels=c("No Change","New Development"),
                      name = "") +
  labs(title = "Land Cover Development Change",
       subtitle = "Atlanta metro area | Fishnet centroids with existing highways\nin dark blue and a new highway in cyan") +
  geom_sf(data=metro_counties, fill=NA, colour="white", size=.75) +
  geom_sf(data = Atlanta_MSA, fill = "transparent",
          color = "black", linewidth = .75) +
  geom_sf(data=AtlantaHighways, color = highlight, linewidth = .5) +
  geom_sf(data=new_highway, colour="cyan", linewidth=1) +
  mapTheme

Now, with the new highways dataset, we can create a new distance-to-highway raster, and from there convert it to fishnet centroids which we merge with the fishnet. This helps us understand how far the fishnet cells are from the new highway.

#new_highway_raster <- 
 #  as(highways_all,'Spatial') %>%
  # rasterize(.,emptyRaster)

#new_highway_raster_distance <- distance(new_highway_raster)
#writeRaster(new_highway_raster_distance, filename=file.path(rast_dir, "new_highway_dist.tif"), format="GTiff", overwrite=TRUE)

new_highway_raster_distance <- raster(paste(dir_git, "187ac7cf231257fc52de0f7f2a1dc2014e4e6f2d/Data/raster/new_highway_dist.tif", sep='/'))

names(new_highway_raster_distance) <- "distance_highways"

new_highwayPoints <-
  rasterToPoints(new_highway_raster_distance) %>%
  as.data.frame() %>%
  st_as_sf(coords = c("x", "y"), crs = st_crs(AtlantaMSA_fishnet))

new_highwayPoints_fishnet <- 
  aggregate(new_highwayPoints, AtlantaMSA_fishnet, mean) %>%
  mutate(distance_highways = ifelse(is.na(distance_highways),0,distance_highways))

ggplot() +
  geom_sf(data=metro_counties) +
  geom_point(data=new_highwayPoints_fishnet, aes(x=xyC(new_highwayPoints_fishnet)[,1], 
                                             y=xyC(new_highwayPoints_fishnet)[,2], 
                 colour=factor(ntile(distance_highways,5))),size=1.5) +
  scale_colour_manual(values = palette5,
                      labels=substr(quintileBreaks(new_highwayPoints_fishnet,"distance_highways"),1,8),
                      name="Quintile\nBreaks") +
  geom_sf(data=highways_all, colour = "black") +
  geom_sf(data=new_highway, colour="cyan", linewidth=.75) +
  geom_sf(data=metro_counties, fill=NA, colour="white", size=.75) +
  geom_sf(data = Atlanta_MSA, fill = "transparent",
          color = "black", linewidth = .75) +
  labs(title = "Distance to Highways",
       subtitle = "As fishnet centroids; Existing highways visualized in black; new highway in cyan") +
  mapTheme

We then use Model 6 to predict how adding a highway might affect development patterns in 2029, based on scenario 1. While this model does not show a true supply-side only effect, it is useful because it allows us to directly compare how adding in a supply-side factor affects the model’s demand-side only predictions.

dat_highway <-
  dat_infill %>%
  dplyr::select(-distance_highways) %>% 
  left_join(as.data.frame(new_highwayPoints_fishnet)) %>% 
  mutate(predict_2029.distance_highways = predict(Model6,. , type="response"))


Map_highway_pred <-
dat_highway %>%
  ggplot() +  
  geom_point(aes(x=xyC(dat_highway)[,1], y=xyC(dat_highway)[,2], colour = factor(ntile(predict_2029.distance_highways,5)))) +
  scale_colour_manual(values = palette5,
                    labels=substr(quintileBreaks(dat_highway,"predict_2029.distance_highways"),1,4),
                    name="% Likelihood\nof Development") +
  geom_sf(data=metro_counties, fill=NA, colour="white", size=.75) +
  geom_sf(data=highways_all, colour = "black") +
  geom_sf(data=new_highway, colour="cyan", linewidth=.75) +
  geom_sf(data = Atlanta_MSA, fill = "transparent",
          color = "black", linewidth = .75) +
  labs(title= "Development Supply in 2029: Predicted Probabilities",
       subtitle = "Based on projected development response to a new highway") +
  mapTheme

grid.arrange(ncol=1,
             map_pred29,
             Map_highway_pred
             )

While the change is slight, we see that adding in the new highway does slightly increase the predicted likelihood of development in the area around it.

Allocation

Now that we better understand the effects of demand-side factors (like population growth and proximity to developed areas) and supply-side factors (like new infrastructure available land), we can analyze these factors by county. This allows us to better understand which counties in the Atlanta MSA are better suited to development than others. It also allows us to see which land use and development policies might be best suited to this region.

Let’s first orient ourselves to how each kind of land cover was distributed in 2019.

dat2 <-
  aggregateRaster(theRasterList19, dat) %>%
  dplyr::select(developed19, forest19, farm19, wetlands19, otherUndeveloped19, water19) %>%
  st_set_geometry(NULL) %>%
  bind_cols(.,dat) %>%
  st_sf() %>%
  st_cast("POLYGON")

rasters19_map <- aggregatedRasters19 %>%
  gather(var,value,developed19:water19) %>%
  st_cast("POLYGON") %>%    #just to make sure no weird geometries slipped in
  mutate(X = xyC(.)$x,
         Y = xyC(.)$y) %>%
  ggplot() +
    geom_sf(data=metro_counties) +
    geom_point(aes(X,Y, colour=as.factor(value))) +
    facet_wrap(~var) +
    scale_colour_manual(values = c(gray, highlight),
                        labels=c("False","True"),
                        name = "") +
    geom_sf(data=metro_counties,
          color = "white",
          fill = "transparent",
          linewidth = .25) +
    geom_sf(data=Atlanta_MSA,
          color = "black",
          fill = "transparent",
          linewidth = .5) +
    labs(title = "Land Cover Types, 2019",
         subtitle = "As fishnet centroids") +
   mapTheme +
   theme()  +
   theme(legend.position = "none")

rasters19_map

We see that much of the metro area, including developed areas, is forested - meaning it has a large amount of tree cover. Farms and other undeveloped lands tend to be located more on the outer edges. Wetlands on the other hand are concentrated mostly in the metro region’s southern counties, along with a number of small ponds and rivers. The largest water bodies, however, are located in the north of the metro.

Mapping these land cover types separately allows us to see that some counties may be better suited to development, while others have more sensitive land cover that should be protected.

However, this method does not allow us to pinpoint where these locations are with much specificity.

Sensitive Land Cover Lost

To better understand where ecologically important lands are being lost for development, we create an indicator layer called sensitive_lost19. This new layer shows where forest or wetlands were lost to development between 2009 and 2019.

dat2 <-
  dat2 %>%
   mutate(sensitive_lost19 = ifelse(forest08 == 1 & forest19 == 0 |
                                    wetlands08 == 1 & wetlands19 == 0,1,0)) %>% 
  st_transform("ESRI:102267")
                      
ggplot() +
  geom_point(data=dat2, aes(x=xyC(dat2)[,1], y=xyC(dat2)[,2], colour=as.factor(sensitive_lost19))) +
  scale_colour_manual(values = c(gray, highlight),
                      labels=c("No Change","Sensitive Lost"),
                      name = "") +
  geom_sf(data = metro_counties, fill = "transparent", color = "white", linewidth = .5) +
  geom_sf(data = Atlanta_MSA, fill = "transparent", color = "black", linewidth = .75) +
  labs(title = "Sensitive lands lost: 2009 - 2019",
       subtitle = "As fishnet centroids") +
  mapTheme

Interestingly, we see that more sensitive areas were lost to development near the metro’s center during this period.

Landscape Fragmentation

To contextualize these sensitive locations, we group areas where the wetlands19 and forest11 rasters are contiguous to create a sensitive_regions layer containing all sensitive regions with an area greater than 1 acre.

sensitiveRegions <- 
  raster::clump(wetlands19 + forest19) %>% #units are meters
  rasterToPolygons() %>%
  st_as_sf() %>%
  group_by(clumps) %>% 
  summarize() %>%
    mutate(Acres = as.numeric(st_area(.) * 0.00024711)) %>% #There are 0.00024711 acres per square meter and 4046.86 square meters in an acre
    filter(Acres > 4046.86)  %>%
  dplyr::select() %>%
  raster::rasterize(.,emptyRaster) 

sensitiveRegions[sensitiveRegions > 0] <- 1  
names(sensitiveRegions) <- "sensitiveRegions"

dat2 <-
  aggregateRaster(c(sensitiveRegions), dat2) %>%
  dplyr::select(sensitiveRegions) %>%
  st_set_geometry(NULL) %>%
  bind_cols(.,dat2) %>%
  st_sf()

ggplot() +
  geom_point(data=dat2, aes(x=xyC(dat2)[,1], y=xyC(dat2)[,2], colour=as.factor(sensitiveRegions))) +
  scale_colour_manual(values = c(gray, highlight),
                      labels=c("Other","Sensitive Regions"),
                      name="") +
  geom_sf(data = metro_counties, fill = "transparent", color = "white", linewidth = .5) +
  geom_sf(data = Atlanta_MSA, fill = "transparent", color = "black", linewidth = .75) +
  labs(title = "Sensitive regions",
       subtitle = "Continous areas of either wetlands or forests\ngreater than 1 acre") +
  mapTheme

This gives us a much more clear understanding of which areas might be most sensitive to development than mapping each type of land cover separately did. We see that most of the counties along the outer border of the Atlanta metro region still have relatively low landscape fragmentation. This suggests that these counties should prioritize infill development over suburban sprawl.

Summarize by County

To better understand how these characteristics compare across counties, we create a matrix of demand side and suitability factors.

This matrix allows us to better understand each county’s suitability for development.

county_specific_metrics <- 
  dat2 %>%
  #predict development demand from our model
  mutate(Development_Demand = predict(Model6, dat2, type="response")) %>%
  #get a count count of grid cells by county which we can use to calculate rates below
  left_join(st_set_geometry(dat, NULL) %>% group_by(NAME) %>% summarize(count = n())) %>%
  #calculate summary statistics by county
  group_by(NAME) %>%
  summarize(Total_Farmland = sum(farm19) / max(count),
            Total_Forest = sum(forest19) / max(count),
            Total_Wetlands = sum(wetlands19) / max(count),
            Total_Undeveloped = sum(otherUndeveloped19) / max(count),
            Sensitive_Land_Lost = sum(sensitive_lost19) / max(count),
            Sensitive_Regions = sum(sensitiveRegions) / max(count),
            Mean_Development_Demand = mean(Development_Demand)) %>%
  #get population data by county
  left_join(countyPopulation_2029 %>% 
            mutate(Population_Change = county_projection_2029 - county_population_2019,
                   Population_Change_Rate = Population_Change / county_projection_2029) %>%
            dplyr::select(NAME,Population_Change_Rate))
county_specific_metrics %>%
  gather(Variable, Value, -NAME, -geometry) %>%
  mutate(Variable = factor(Variable, levels=c("Population_Change_Rate","Mean_Development_Demand",
                                              "Total_Farmland","Total_Undeveloped","Total_Forest",
                                              "Total_Wetlands","Sensitive_Land_Lost","Sensitive_Regions",
                                              ordered = TRUE))) %>%
  mutate(Planning_Designation = case_when(
    Variable == "Population_Change_Rate" | Variable == "Mean_Development_Demand" ~ "Demand-Side",
    Variable == "Total_Farmland" | Variable == "Total_Undeveloped"               ~ "Suitable",
    TRUE                                                                         ~ "Not Suitable")) %>%
  ggplot(aes(x=Variable, y=Value, fill=Planning_Designation)) +
    geom_bar(stat="identity", position=position_dodge(), colour="black") +
    facet_wrap(~NAME, ncol=5) +
    coord_flip() +
    scale_y_continuous(breaks = seq(.25, 1, by = .25)) +
    geom_vline(xintercept = 2.5) + geom_vline(xintercept = 4.5) +
    scale_fill_manual(values=c("#0D0887FF","#BD3786FF","#FDC926FF")) +
    labs(title= "County Specific Allocation Metrics", subtitle= "As rates", x="Indicator", y="Rate") +
    plotTheme + theme(axis.text.x = element_text(angle = 45, hjust = 1), legend.position="bottom")

This chart shows three types of metrics:
- Demand-side factors
- Factors suitable for development, and
- Factors not suitable for development

The demand side factors are Mean_Development_Demand and Population_Change_Rate. Mean_Development_Demand is the mean value of predicted development demand for a given county in 2029. Population_Change_Rate is the share of a county’s population that is projected to move-in between 2019 and 2029.

Factors suitable for development are area of Total_Farmland and area of Total_Undeveloped land in a given county.

Factors not suitable for development are area of Sensitive_Regions, Sensitive_Land_Lost, and Total_Wetlands in a county.

In determining our allocation criteria, we determined any area with a wetland was not suitable for development. Attending to the externalities of urban sprawl, we chose to preserve these vital ecosystems. When it comes to forests, however, given that the Atlanta MSA has such extensive tree cover, we determined that forested areas are suitable for development.

Looking at the development suitability indicators across the 21 counties of Georgia’s MSA, we see that all counties have a large percentage of forested land and therefore of sensitive regions. Looking at the rest of the indicators together however, we can see that some counties are better suited for development than others.

For example, we see that Hall County has both a high rate of development demand and of population change. It also has a relatively large amount of farmland and a relatively low amount of total wetlands and sensitive land lost. Together, these factors suggest that Hall County has ample space for development that will not impact sensitive areas (other than forest).

The satellite image below of development in Hall County shows why there is a large amount of forested land cover across the Atlanta metro region. There seems to be a regional preference for dispersed development in forested land.

On the other hand, Clayton County appears to be poorly suited for development because it has relatively high development demand, is experiencing population growth, has a low amount of farmland and undeveloped land, and has a relatively high amount of wetlands.

Let’s look more closely at Hall County (in the North) and Clayton County (to the South) to see what these traits look like spatially.

ggplot() +
  geom_sf(data = metro_counties, fill = gray, color = "white", linewidth = .5) +
  geom_sf(data = filter(metro_counties, NAME=="Hall" | NAME=="Clayton"), fill = "#FDC926FF", color = "white", linewidth = .5) +
  geom_sf(data = Atlanta_MSA, fill = "transparent", color = "black", linewidth = .75) +
  labs(title = "Atlanta MSA",
       subtitle = "Hall and Clayton Counties Highlighted") +
  mapTheme

Hall County

First, we map supply and demand factors for Hall County, to examine an example of a county well suited to further development.

HallCounty <-
  dat2 %>%
    mutate(Development_Demand = predict(Model6, dat2, type="response")) %>%
    filter(NAME == "Hall") %>%
    rename("developed19" = developed19...2)

HallCounty_landUse <- rbind(
  filter(HallCounty, wetlands19 == 1 ) %>%
  dplyr::select() %>% mutate(Land_Use = "Not Suitable"),
  filter(HallCounty, developed19 == 1) %>%
  dplyr::select() %>% mutate(Land_Use = "Developed"))

Map_Hall1 <-
ggplot() +
  geom_sf(data=HallCounty, aes(fill=factor(ntile(Development_Demand,5))), colour=NA) +
  geom_point(data=HallCounty_landUse, aes(x=xyC(HallCounty_landUse)[,1], 
                                        y=xyC(HallCounty_landUse)[,2], colour=Land_Use),
                                        shape = 15, size = 3) +
  geom_sf(data=st_intersection(AtlantaHighways,filter(metro_counties, NAME=="Hall")), linewidth=1, color = darkGray) +
  scale_fill_manual(values = palette5, name="Development\nDemand",
                    labels=substr(quintileBreaks(HallCounty,"Development_Demand"),1,5)) +
  scale_colour_manual(values = c(gray,"#F0F921FF")) + 
  geom_sf(data = filter(metro_counties, NAME=="Hall"), fill = "transparent", color = "black", linewidth = 1) +
  labs(title = "Development Potential, 2029: Hall County",
       subtitle = "With Developed and Unsuitable Areas from 2019") +
  mapTheme +
  guides(fill = guide_legend(order = 1), colour = guide_legend(order = 2))

Map_Hall2 <-
ggplot() +
  geom_sf(data=HallCounty, aes(fill=factor(ntile(pop_Change,5))), colour=NA) +
  geom_point(data=HallCounty_landUse, aes(x=xyC(HallCounty_landUse)[,1], 
                                        y=xyC(HallCounty_landUse)[,2], colour=Land_Use),
                                        shape = 15, size = 3) +
  geom_sf(data=st_intersection(AtlantaHighways, filter(metro_counties, NAME=="Hall")), linewidth=1, color = darkGray) +
  scale_fill_manual(values = palette5, name="Population\nChange",
                    labels=substr(quintileBreaks(HallCounty,"pop_Change"),1,5)) +
  scale_colour_manual(values = c(gray,"#F0F921FF")) + 
  geom_sf(data = filter(metro_counties, NAME=="Hall"), fill = "transparent", color = "black", linewidth = 1) +
  labs(title = "Projected Population, 2029: Hall County",
       subtitle = "With Developed and Unsuitable Areas from 2019") +
  mapTheme +
  guides(fill = guide_legend(order = 1), colour = guide_legend(order = 2))

grid.arrange(
  Map_Hall1,
  Map_Hall2,
  ncol=2)

Mapping Hall County’s development potential and projected population with developed land and wetlands (the “not suitable” land) shows us that the County still has a significant amount of area that is suitable for development. In particular, this county would do well to prioritize infill development between highways in its northwest and southern portions where there is predicted to be both high development demand and significant population growth. This would also help to reduce forest fragmentation and pressure on wetlands in the county’s east and northeast.

Clayton County

Next, we map the same supply and demand factors for Clayton County to examine an example of a county not well suited to further development.

ClaytonCounty <-
  dat2 %>%
    mutate(Development_Demand = predict(Model6, dat2, type="response")) %>%
    filter(NAME == "Clayton") %>%
    rename("developed19" = developed19...2)

ClaytonCounty_landUse <- rbind(
  filter(ClaytonCounty, wetlands19 == 1 ) %>%
  dplyr::select() %>% mutate(Land_Use = "Not Suitable"),
  filter(ClaytonCounty, developed19 == 1) %>%
  dplyr::select() %>% mutate(Land_Use = "Developed"))

grid.arrange(
ggplot() +
  geom_sf(data=ClaytonCounty, aes(fill=factor(ntile(Development_Demand,5))), colour=NA) +
  geom_point(data=ClaytonCounty_landUse, aes(x=xyC(ClaytonCounty_landUse)[,1], 
                                        y=xyC(ClaytonCounty_landUse)[,2], colour=Land_Use),
                                        shape = 15, size = 6) +
  geom_sf(data=st_intersection(AtlantaHighways,filter(metro_counties, NAME=="Clayton")), linewidth=1, color = darkGray) +
  scale_fill_manual(values = palette5, name="Development\nDemand",
                    labels=substr(quintileBreaks(ClaytonCounty,"Development_Demand"),1,5)) +
  scale_colour_manual(values = c(gray,"#F0F921FF")) + 
  geom_sf(data = filter(metro_counties, NAME=="Clayton"), fill = "transparent", color = "black", linewidth = 1) +
  labs(title = "Development Potential, 2029: Clayton County",
       subtitle = "With Developed and Unsuitable Areas from 2019") + 
  mapTheme +
  guides(fill = guide_legend(order = 1), colour = guide_legend(order = 2)),

ggplot() +
  geom_sf(data=ClaytonCounty, aes(fill=factor(ntile(pop_Change,5))), colour=NA) +
  geom_point(data=ClaytonCounty_landUse, aes(x=xyC(ClaytonCounty_landUse)[,1], 
                                        y=xyC(ClaytonCounty_landUse)[,2], colour=Land_Use),
                                        shape = 15, size = 6) +
  geom_sf(data=st_intersection(AtlantaHighways, filter(metro_counties, NAME=="Clayton")), linewidth=1, color = darkGray) +
  scale_fill_manual(values = palette5, name="Population\nChange",
                    labels=substr(quintileBreaks(ClaytonCounty, "pop_Change"), 1, 5)) +
  scale_colour_manual(values = c(gray,"#F0F921FF")) + 
  geom_sf(data = filter(metro_counties, NAME=="Clayton"), fill = "transparent", color = "black", linewidth = 1) +
  labs(title = "Projected Population, 2029: Clayton County",
      subtitle = "With Developed and Unsuitable Areas from 2019") + 
  mapTheme +
  guides(fill = guide_legend(order = 1), colour = guide_legend(order = 2)), ncol=2)

In contrast to Hall County’s relatively large amount of land that is suitable for development, we see that Clayton County has almost none. The county is almost entirely developed and the two areas in the southern portion of the county that are not developed are dominated by wetland areas that are not suitable for development.

Furthermore, the map shows that the remaining cells are all near wetland areas or not expected to see population growth. Therefore, Clayton County is an ideal location for conservation rather than development in the Atlanta metro area.

Conclusion

Thank you for walking through our model. We conclude by summarizing the strengths, weaknesses, and planning implications of this approach.

Strengths of approach
While not perfect, the strengths of this approach are that it allows us to estimate future urban sprawl based on land cover and population characteristics of a given area. Being able to visually analyze development potential through maps and charts has great utility for communicating the complex nature of urban sprawl to everyone from policymakers to community members with non-technical backgrounds.

Weaknesses
While an incredibly useful tool, because this approach only takes inputs from two study years, it has the potential to predict future development based on a small historical sample and anomalous trends. Furthermore, while the maps and charts are helpful for communication, they are estimations at best, and only one piece of the land allocation toolkit. The convincing nature of these graphics risks communicating these estimates as fact. Finally, the model is only as good as the variables within it. In future studies, we could improve our model by testing additional predictor variables such as school districts, demographic information, and real estate values.

Policy implications
Based on the results of our analysis, the 21 county Atlanta metro region would benefit from conservation easements to prevent further landscape fragmentation. This would protect the vitality of forest ecosystems over the long-term. To calibrate against further landscape fragmentation, this analysis suggests that policymakers could implement an urban growth boundary in the fringe counties to protect the metro’s remaining sensitive regions before they are fully developed. This would have the added benefit of encouraging infill development in the MSA.

LS0tDQp0aXRsZTogIkxVRU0gQTUgLSBGb3JlY2FzdGluZyBTcHJhd2wiDQphdXRob3I6ICJMaW5kc2V5IEhvdmVyICYgQ2hhcmxpZSBUb3duc2xleSINCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCINCm91dHB1dDogDQogIGh0bWxfZG9jdW1lbnQ6DQogICAgdG9jOiB0cnVlDQogICAgdG9jX2RlcHRoOiAzDQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgdGhlbWU6IGZsYXRseQ0KICAgIGhpZ2hsaWdodDogYnJlZXplZGFyaw0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkNCmtuaXRyOjpvcHRzX2NodW5rJHNldCh3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSkNCnJtKGxpc3Q9bHMoKSkNCg0Kb3B0aW9ucyhzY2lwZW4gPSA5OTkpICN0dXJuIG9mZiBzY2llbnRpZmljIG5vdGF0aW9uDQpgYGANCg0KYGBge3IgbGlicmFyaWVzIGFuZCB0aGVtZXN9DQojaW5zdGFsbC5wYWNrYWdlcygiRk5OIikNCiNpbnN0YWxsLnBhY2thZ2VzKCJnZ3JlcGVsIikNCiNpbnN0YWxsLnBhY2thZ2VzKCJ5YXJkc3RpY2siKQ0KI2luc3RhbGwucGFja2FnZXMoImlncmFwaCIpDQojaW5zdGFsbC5wYWNrYWdlcygiY2FyZXQiKQ0KI3VwZGF0ZS5wYWNrYWdlcygiY2FyZXQiKQ0KI2luc3RhbGwucGFja2FnZXMoInJlYWN0YWJsZSIpDQoNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShzZikNCmxpYnJhcnkocmFzdGVyKQ0KbGlicmFyeShrbml0cikNCmxpYnJhcnkoa2FibGVFeHRyYSkNCmxpYnJhcnkodGlkeWNlbnN1cykNCmxpYnJhcnkodGlncmlzKQ0KbGlicmFyeShGTk4pDQpsaWJyYXJ5KGNhcmV0KQ0KbGlicmFyeSh5YXJkc3RpY2spDQpsaWJyYXJ5KHBzY2wpDQpsaWJyYXJ5KHBsb3RST0MpIA0KbGlicmFyeShnZ3JlcGVsKQ0KbGlicmFyeShwUk9DKQ0KbGlicmFyeShncmlkKQ0KbGlicmFyeShncmlkRXh0cmEpDQpsaWJyYXJ5KHZpcmlkaXMpDQpsaWJyYXJ5KGlncmFwaCkNCmxpYnJhcnkobWFwdG9vbHMpDQpsaWJyYXJ5KHJlYWN0YWJsZSkNCmxpYnJhcnkoY2xhc3NJbnQpDQoNCnBsb3RUaGVtZSA8LSB0aGVtZSgNCiAgcGxvdC50aXRsZSA9ZWxlbWVudF90ZXh0KHNpemU9MTIpLA0KICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9OCksDQogIHBsb3QuY2FwdGlvbiA9IGVsZW1lbnRfdGV4dChzaXplID0gNiksDQogIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCwgYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSwNCiAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwNCiAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksDQogICMgU2V0IHRoZSBlbnRpcmUgY2hhcnQgcmVnaW9uIHRvIGJsYW5rDQogIHBhbmVsLmJhY2tncm91bmQ9ZWxlbWVudF9ibGFuaygpLA0KICBwbG90LmJhY2tncm91bmQ9ZWxlbWVudF9ibGFuaygpLA0KICAjcGFuZWwuYm9yZGVyPWVsZW1lbnRfcmVjdChjb2xvdXI9IiNGMEYwRjAiKSwNCiAgIyBGb3JtYXQgdGhlIGdyaWQNCiAgcGFuZWwuZ3JpZC5tYWpvcj1lbGVtZW50X2xpbmUoY29sb3VyPSIjRDBEMEQwIixzaXplPS43NSksDQogIGF4aXMudGlja3M9ZWxlbWVudF9ibGFuaygpKQ0KDQptYXBUaGVtZSA8LSB0aGVtZShwbG90LnRpdGxlID1lbGVtZW50X3RleHQoc2l6ZT0xMiksDQogICAgICAgICAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9OCksDQogICAgICAgICAgICAgICAgICBwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoc2l6ZSA9IDYpLA0KICAgICAgICAgICAgICAgICAgYXhpcy5saW5lPWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICAgICAgICAgIGF4aXMudGV4dC54PWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICAgICAgICAgIGF4aXMudGV4dC55PWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICAgICAgICAgIGF4aXMudGlja3M9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgICAgICAgICAgYXhpcy50aXRsZS54PWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUueT1lbGVtZW50X2JsYW5rKCksDQogICAgICAgICAgICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kPWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICAgICAgICAgIHBhbmVsLmJvcmRlcj1lbGVtZW50X2JsYW5rKCksDQogICAgICAgICAgICAgICAgICBwYW5lbC5ncmlkLm1ham9yPWVsZW1lbnRfbGluZShjb2xvdXIgPSAndHJhbnNwYXJlbnQnKSwNCiAgICAgICAgICAgICAgICAgIHBhbmVsLmdyaWQubWlub3I9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgICAgICAgICAgbGVnZW5kLmRpcmVjdGlvbiA9ICJ2ZXJ0aWNhbCIsIA0KICAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IiwNCiAgICAgICAgICAgICAgICAgIHBsb3QubWFyZ2luID0gbWFyZ2luKDEsIDEsIDEsIDEsICdjbScpLA0KICAgICAgICAgICAgICAgICAgbGVnZW5kLmtleS5oZWlnaHQgPSB1bml0KDEsICJjbSIpLCBsZWdlbmQua2V5LndpZHRoID0gdW5pdCgwLjIsICJjbSIpKQ0KDQojQ29sb3JzOiBWaXJpZGlzIFBsYXNtYSAob3B0aW9uICJDIikNCmdyYXkgPC0gIiNEM0QzRDMiDQpkYXJrR3JheSA8LSAiIzUwNTI1MSINCmhpZ2hsaWdodCA8LSAiIzBEMDg4N0ZGIg0KcGFsZXR0ZTIgPC0gYygiI0ZBOUUzQkZGIiwgIiMwRDA4ODdGRiIpDQpwYWxldHRlMyA8LSBjKCIjRkRDOTI2RkYiLCAiI0Q4NTc2QkZGIiwgIiMwRDA4ODdGRiIpDQpwYWxldHRlNCA8LSBjKCIjRkE5RTNCRkYiLCAiI0Q4NTc2QkZGIiwgIiM5QzE3OUVGRiIsICIjMEQwODg3RkYiKQ0KcGFsZXR0ZTUgPC0gYygiI0ZEQzkyNkZGIiwgIiNFRDc5NTNGRiIsICIjRDg1NzZCRkYiLCAiIzlDMTc5RUZGIiwgIiMwRDA4ODdGRiIpDQpwYWxldHRlMTAgPC0gYygiI0YwRjkyMUZGIiwgIiNGREM5MjZGRiIsICIjRkE5RTNCRkYiLCAiI0VENzk1M0ZGIiwgIiNEODU3NkJGRiIsDQogICAgICAgICAgICAgICAiI0JEMzc4NkZGIiwgIiM5QzE3OUVGRiIsICIjNzMwMUE4RkYiLCAiIzQ3MDM5RkZGIiwgIiMwRDA4ODdGRiIpDQoNCiNSIE1hcmtkb3duIENoZWF0IHNoZWV0IGZvciByZWZlcmVuY2UgLSBodHRwczovL3d3dy5yc3R1ZGlvLmNvbS93cC1jb250ZW50L3VwbG9hZHMvMjAxNS8wMi9ybWFya2Rvd24tY2hlYXRzaGVldC5wZGYNCmBgYA0KDQpgYGB7ciBkaXJlY3RvcmllcywgaW5jbHVkZT1GQUxTRX0NCmRpcl9naXQgPC0gImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9jLXRvd25zbGV5L0xVRU1fQTVfUmVwbyINCg0KZGlyX2N0IDwtICdDOi9Vc2Vycy9jdG93bi9PbmVEcml2ZSAtIFBlbm5PMzY1L0NsYXNzZXMvQ2xhc3Nlc19TZW00XzIwMjNTcHJpbmcvQ1BMTiA2NzVfTGFuZCBNb2RlbGluZy9Bc3NpZ25tZW50cy9BNV9TcHJhd2xGb3JlY2FzdGluZy9MVUVNX0E1X1JlcG8vRGF0YScNCmBgYA0KDQpgYGB7ciBmdW5jdGlvbnN9DQojdGhpcyBmdW5jdGlvbiBjb252ZXJ0cyBhIGNvbHVtbiBpbiB0byBxdWludGlsZXMuIEl0IGlzIHVzZWQgZm9yIG1hcHBpbmcuDQpxdWludGlsZUJyZWFrcyA8LSBmdW5jdGlvbihkZix2YXJpYWJsZSkgew0KICAgIGFzLmNoYXJhY3RlcihxdWFudGlsZShkZltbdmFyaWFibGVdXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgYyguMDEsLjIsLjQsLjYsLjgpLG5hLnJtPVQpKQ0KfQ0KDQojVGhpcyBmdW5jdGlvbiBjYW4gYmUgdXNlZCB0byBjb252ZXJ0IGEgcG9seWdvbiBzZiB0byBjZW50cm9pZHMgeHkgY29vcmRzLg0KeHlDIDwtIGZ1bmN0aW9uKGFQb2x5Z29uU0YpIHsNCiAgYXMuZGF0YS5mcmFtZSgNCiAgICBjYmluZCh4PXN0X2Nvb3JkaW5hdGVzKHN0X2NlbnRyb2lkKGFQb2x5Z29uU0YpKVssMV0sDQogICAgICAgICAgeT1zdF9jb29yZGluYXRlcyhzdF9jZW50cm9pZChhUG9seWdvblNGKSlbLDJdKSkNCn0gDQoNCiN0aGlzIGZ1bmN0aW9uIGNvbnZlcnQgYSByYXN0ZXIgdG8gYSBkYXRhIGZyYW1lIHNvIGl0IGNhbiBiZSBwbG90dGVkIGluIGdncGxvdA0KcmFzdCA8LSBmdW5jdGlvbihpblJhc3Rlcikgew0KICBkYXRhLmZyYW1lKA0KICAgIHh5RnJvbUNlbGwoaW5SYXN0ZXIsIDE6bmNlbGwoaW5SYXN0ZXIpKSwgDQogICAgdmFsdWUgPSBnZXRWYWx1ZXMoaW5SYXN0ZXIpKSB9DQoNCiN0aGlzIGZ1bmN0aW9uIGFnZ3JlZ2F0ZXMgcmFzdGVycw0KYWdncmVnYXRlUmFzdGVyIDwtIGZ1bmN0aW9uKGlucHV0UmFzdGVyTGlzdCwgQXRsYW50YU1TQV9maXNobmV0KSB7DQogICNjcmVhdGUgYW4gZW1wdHkgZmlzaG5ldCB3aXRoIHRoZSBzYW1lIGRpbWVuc2lvbnMgYXMgdGhlIGlucHV0IGZpc2huZXQNCiAgdGhlc2VGaXNobmV0cyA8LSBBdGxhbnRhTVNBX2Zpc2huZXQgJT4lIGRwbHlyOjpzZWxlY3QoKQ0KICAjZm9yIGVhY2ggcmFzdGVyIGluIHRoZSByYXN0ZXIgbGlzdA0KICBmb3IgKGkgaW4gaW5wdXRSYXN0ZXJMaXN0KSB7DQogICNjcmVhdGUgYSB2YXJpYWJsZSBuYW1lIGNvcnJlc3BvbmRpbmcgdG8gdGhlIGl0aCByYXN0ZXINCiAgdmFyTmFtZSA8LSBuYW1lcyhpKQ0KICAjY29udmVydCByYXN0ZXIgdG8gcG9pbnRzIGFzIGFuIHNmDQogICAgdGhlc2VQb2ludHMgPC0NCiAgICAgIHJhc3RlclRvUG9pbnRzKGkpICU+JQ0KICAgICAgYXMuZGF0YS5mcmFtZSgpICU+JQ0KICAgICAgc3RfYXNfc2YoY29vcmRzID0gYygieCIsICJ5IiksIGNycyA9IHN0X2NycyhBdGxhbnRhTVNBX2Zpc2huZXQpKSAlPiUNCiAgICAgIGZpbHRlciguW1sxXV0gPT0gMSkNCiAgI2FnZ3JlZ2F0ZSB0byB0aGUgZmlzaG5ldA0KICAgIHRoaXNGaXNobmV0IDwtDQogICAgICBhZ2dyZWdhdGUodGhlc2VQb2ludHMsIEF0bGFudGFNU0FfZmlzaG5ldCwgbGVuZ3RoKSAlPiUNCiAgICAgIG11dGF0ZSghIXZhck5hbWUgOj0gaWZlbHNlKGlzLm5hKC5bWzFdXSksMCwxKSkNCiAgI2FkZCB0byB0aGUgbGFyZ2VyIGZpc2huZXQNCiAgICB0aGVzZUZpc2huZXRzIDwtIGNiaW5kKHRoZXNlRmlzaG5ldHMsIHRoaXNGaXNobmV0KQ0KICB9DQogICNvdXRwdXQgYWxsIGFnZ3JlZ2F0ZXMgYXMgb25lIGxhcmdlIGZpc2huZXQNCiAgIHJldHVybih0aGVzZUZpc2huZXRzKQ0KfQ0KYGBgDQoNCiMgSW50cm9kdWN0aW9uOiBUaGlzIE1vZGVsIGFuZCBJdHMgU2lnbmlmaWNhbmNlIHRvIFBsYW5uZXJzDQoNCldlbGNvbWUhIEluIHRoaXMgaW50ZXJhY3RpdmUgUi1tYXJrZG93biBtZW1vLCB3ZSBlbXBsb3kgaGlzdG9yaWNhbCBwYXR0ZXJucyBvZiBkZXZlbG9wbWVudCBhbmQgcG9wdWxhdGlvbiB0cmVuZHMgaW4gdGhlIEF0bGFudGEsIEdlb3JnaWEgbWV0cm9wb2xpdGFuIHN0YXRpc3RpY2FsIGFyZWEgKCJNU0EiKSB0byBwcmVkaWN0IGZ1dHVyZSBkZXZlbG9wbWVudCBzY2VuYXJpb3MuIFNwZWNpZmljYWxseSwgd2UgZXhhbWluZSB0aGUgc3BhdGlhbCByZWxhdGlvbnNoaXBzIHRoYXQgZXhpc3QgYW1vbmcgcG9wdWxhdGlvbiBncm93dGggYW5kIGhpZ2h3YXkgaW5mcmFzdHJ1Y3R1cmUgd2l0aCBsYW5kIGRldmVsb3BtZW50LiBUaHJvdWdoIHRoZXNlIG1vZGVscywgd2UgZXhwbG9yZSB0aGUgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIGVhY2ggb2YgdGhlc2UgaW5kZXBlbmRlbnQgdmFyaWFibGVzIGFuZCB1cmJhbiBzcHJhd2wsIG91ciBkZXBlbmRlbnQgdmFyaWFibGUuDQoNClVyYmFuIHNwcmF3bCBpcyBhIGRldmVsb3BtZW50IHNjZW5hcmlvIGNoYXJhY3Rlcml6ZWQgYnkgImxlYXAgZnJvZ2dpbmciIGRldmVsb3BtZW50LCBhIGRldmVsb3BtZW50IHNjZW5hcmlvIHRoYXQgZGlzcHJvcG9ydGlvbmF0ZWx5IGNvbnN1bWVzIGxhbmQgYW5kIGVudmlyb25tZW50YWwgcmVzb3VyY2VzLCB3aXRoIHNpZ25pZmljYW50IG5lZ2F0aXZlIGVjb2xvZ2ljYWwgZXh0ZXJuYWxpdGllcy4gVGhlc2UgbW9kZWxzIHdpbGwgcHJlZGljdCBsaWtlbHkgdXJiYW4gc3ByYXdsIGZvciB0aGUgQXRsYW50YSBtZXRybyBhcmVhIGJhc2VkIG9uIHR3byBzY2VuYXJpb3MuIFRoZSBtb2RlbHMgcHJlc2VudGVkIGhlcmUgZW5hYmxlIHBsYW5uZXJzIGFuZCBwb2xpY3kgbWFrZXJzIHRvIHByZWVtcHRpdmVseSBkZXZlbG9wIHBvbGljaWVzIHRvIGNhbGlicmF0ZSBhZ2FpbnN0IHVyYmFuIHNwcmF3bCwgZW1wb3dlcmluZyBtdW5pY2lwYWwgZ292ZXJubWVudHMgdG8gY3JlYXRlIGNvbnNjaWVudGlvdXMgbGFuZCBhbGxvY2F0aW9uIHBsYW5zLg0KDQpUaGUgbW9kZWwgZGV2ZWxvcGVkIHdpbGwgcHJlZGljdCB0d28gZGlmZmVyZW50IHNjZW5hcmlvczogZGVtYW5kLXNpZGUgYW5kIHN1cHBseS1zaWRlLiBJbiB0aGUgZGVtYW5kLXNpZGUgbW9kZWwsIHdlIGF0dGVtcHQgdG8gdW5kZXJzdGFuZCB0aGUgcmVsYXRpb25zaGlwIHRoYXQgcG9wdWxhdGlvbiBncm93dGggaGFzIHdpdGggdXJiYW4gc3ByYXdsIGluIHRoZSBNU0EuIFNwZWNpZmljYWxseSwgd2UgcHJlZGljdCB0aGUgZGVtYW5kIGZvciBkZXZlbG9wbWVudCBhY2NvcmRpbmcgdG8gcG9wdWxhdGlvbiBncm93dGgsIGFuZCBob3cgdGhpcyB3b3VsZCBzcGF0aWFsbHkgbWFuaWZlc3QgYmFzZWQgb24gZXhpc3RpbmcgbGFuZCB1c2UgdHJlbmRzIGFuZCBwb3B1bGF0aW9uIHByb2plY3Rpb25zLiBUaGUgc3VwcGx5LXNpZGUgbW9kZWwgd2lsbCBwcmVkaWN0IGhvdyBmdXR1cmUgZGV2ZWxvcG1lbnQgbWlnaHQgb2NjdXIgZm9sbG93aW5nIHRoZSBjb25zdHJ1Y3Rpb24gb2YgYSBuZXcsIHRoZW9yZXRpY2FsIGhpZ2h3YXksIHN1cHBseWluZyBpbmZyYXN0cnVjdHVyZS4gVGhyb3VnaCB0aGlzIHNjZW5hcmlvLCB3ZSBhdHRlbXB0IHRvIGFzY2VydGFpbiB0aGUgcmVsYXRpb25zaGlwIHRoYXQgaGlnaHdheSBkZXZlbG9wbWVudCBoYXMgd2l0aCB1cmJhbiBzcHJhd2wuDQoNCkxldCdzIGV4cGxvcmUgaG93IHNwcmF3bCBjYW4gb2NjdXIsIHVubWl0aWdhdGVkLCBiYXNlZCBvbiB0aGVzZSBpbmRlcGVuZGVudCB2YXJpYWJsZXMuIFdlIHVzZSBsYW5kIGNvdmVyIGFuZCBwb3B1bGF0aW9uIGRhdGEgZnJvbSB0aGUgeWVhcnMgMjAwOC8yMDA5IGFuZCAyMDE5IHRvIHByZWRpY3QgZm9yIHRoZSB5ZWFyIDIwMjkuDQoNCi0gICBSZWdpb24gb2YgaW50ZXJlc3Q6IEF0bGFudGEsIEdBIG1ldHJvDQoNCi0gICBZZWFycyBvZiBzdHVkeTogMjAwOC0yMDE5DQoNCi0gICBQcm9qZWN0aW5nIHRvIDIwMjkNCg0KLSAgIERhdGEgbGV2ZWw6IGZpc2huZXQgZ3JpZCBjZWxsDQoNClRoZSBmb2xsb3dpbmcgY2h1bmtzIG9mIGNvZGUgd2lsbCBjb2xsZWN0IHRoZSBwcmVyZXF1aXNpdGUgZGF0YSBuZWVkZWQgZm9yIHRoZSBtb2RlbCBmcm9tIHRoZSB0d28geWVhcnMgb2Ygc3R1ZHksIHN0cnVjdHVyZSB0aGUgZGF0YSBpbiBwcmVwYXJhdGlvbiBmb3IgbW9kZWxpbmcsIGFuZCBydW4gdGhlIGRlbWFuZCAtIGFuZCBzdXBwbHktIHNpZGUgbW9kZWxzIGJhc2VkIG9uIHRoZSBzdHVkeSB5ZWFycycgZGF0YSB0byBwcm9qZWN0IGZvciB0aGUgeWVhciAyMDMwLg0KDQojIERhdGEgR2F0aGVyaW5nIGFuZCBGZWF0dXJlIEVuZ2luZWVyaW5nDQoNCkZpcnN0LCB3ZSBwdWxsIGluIGFuZCB2aXN1YWxpemUgb3VyIHNwYXRpYWwgZGF0YXNldHMgb2YgdGhlIEF0bGFudGEgTVNBJ3MgMjEgY291bnRpZXMuDQoNCmBgYHtyIGNyZWF0aW5nIEdlb0pTT04gZnJvbSBTaGFwZWZpbGUsIGluY2x1ZGU9RkFMU0UsIGV2YWw9RkFMU0V9DQojU0hQMSA8LSBzdF9yZWFkKCJDOi9Vc2Vycy9MaW5kc2V5L0Rlc2t0b3AvQ1BMTjY3NV9MYW5kX1VzZV9Nb2RlbGluZ19EZXNrdG9wL0ZpbmFsL0xVRU1fQTVfUmVwby9TaGFwZWZpbGVzL1NIUDEuc2hwIikNCg0KI0dlb0pTT04xIDwtIHN0X3dyaXRlKFNIUDEsICJDOi9Vc2Vycy9MaW5kc2V5L0Rlc2t0b3AvQ1BMTjY3NV9MYW5kX1VzZV9Nb2RlbGluZ19EZXNrdG9wL0ZpbmFsL0xVRU1fQTVfUmVwby9HZW9KU09Ocy9TSFAxLmdlb2pzb24iKQ0KDQojR2VvSlNPTl94IDwtIHN0X3JlYWQoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9jLXRvd25zbGV5L0xVRU1fQTVfUmVwby9kOTg1OWZjMTA2OWRkODlkNDRmY2FmMzQ1ZTcyMmE5YmY2MDFhNTUwL0RhdGEvZ2VvanNvbi9HZW9KU09OX3guZ2VvanNvbiIpICMjIyBzcGVjaWZ5IHRoZSBmaWxlIG5hbWUgKG5vdGU6IHRoaXMgaXMgdGhlIHBlcm1hbGluaywgYnV0IHlvdSBjaGFuZ2UgdGhlIGRvbWFpbiB0byByYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tIEFORCB5b3UgcmVtb3ZlIC9ibG9iLykNCg0KDQojQXRsYW50YV9tZXRyb19ib3VuZGF5IDwtIHN0X3JlYWQoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9jLXRvd25zbGV5L0xVRU1fQTVfUmVwby9kOTg1OWZjMTA2OWRkODlkNDRmY2FmMzQ1ZTcyMmE5YmY2MDFhNTUwL0RhdGEvZ2VvanNvbi9BdGxhbnRhX01ldHJvX0JvdW5kYXJ5Lmdlb2pzb24iKQ0KDQpgYGANCg0KQW5kIHJldHJpZXZlIHRoZSBBdGxhbnRhIE1TQSBib3VuZGFyeSB1c2luZyB0aGUgYHRpZ3Jpc2AgcGFja2FnZS4NCg0KYGBge3IgQXRsYW50YSBNU0Egc2hhcGVmaWxlLCByZXN1bHRzPSdoaWRlJ30NCiNMaXN0IG9mIHRoZSBBdGxhbnRhIHJlZ2lvbiBNUE9zIDIxIGNvdW50aWVzDQpDb3VudGllcyA8LSBjKCJCYXJyb3ciLCAiQmFydG93IiwgIkNhcnJvbGwiLCAiQ2hlcm9rZWUiLCANCiAgICAgICAgICAgICAgIkNsYXl0b24iLCAiQ29iYiIsICJDb3dldGEiLCAiRGF3c29uIiwgDQogICAgICAgICAgICAgICJEZUthbGIiLCAiRG91Z2xhcyIsICJGYXlldHRlIiwgIkZvcnN5dGgiLCANCiAgICAgICAgICAgICAgIkZ1bHRvbiIsICJHd2lubmV0dCIsICJIYWxsIiwgIkhlbnJ5IiwgDQogICAgICAgICAgICAgICJOZXd0b24iLCAiUGF1bGRpbmciLCAiUm9ja2RhbGUiLCAiU3BhbGRpbmciLCAiV2FsdG9uIikNCg0KI0ZpcnN0IHB1bGwgYWxsIE1TQXMgaW4gdGhlIFUuUy4sIHRoZW4gc2VhcmNoIGZvciB5b3VycyBhbmQgZmlsdGVyIHdpdGggdGhlIGNvcnJlY3QgQ0JTQUZQIGNvZGUNCm1ldHJvX2NvdW50aWVzIDwtIGNvdW50aWVzKHN0YXRlID0gIkdBIiwgY2IgPSBUUlVFLCByZXNvbHV0aW9uID0gIjUwMGsiLCB5ZWFyID0gTlVMTCkgJT4lDQogIGZpbHRlcihOQU1FICVpbiUgQ291bnRpZXMpICU+JSANCiAgc3RfdHJhbnNmb3JtKCJFU1JJOjEwMjI2NyIpDQoNCiNNZXJnZSBjb3VudGllcyBpbnRvIG9uZSBzaGFwZWZpbGUgc2hvd2luZyBqdXN0IHRoZSBib3VuZGFyeQ0KQXRsYW50YV9NU0EgPC0gc3RfdW5pb24obWV0cm9fY291bnRpZXMpICU+JSANCiAgc3RfdHJhbnNmb3JtKCJFU1JJOjEwMjI2NyIpDQpgYGANCg0KYGBge3IgcGxvdCBtc2F9DQojcGxvdCBNU0EgYm91bmRhcnkgdG8gbWFrZSBzdXJlIGl0cyByaWdodA0KZ2dwbG90KCkgKyANCiAgZ2VvbV9zZihkYXRhID0gbWV0cm9fY291bnRpZXMpICsNCiAgZ2VvbV9zZihkYXRhID0gQXRsYW50YV9NU0EsIA0KICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwgZmlsbCA9ICJ0cmFuc3BhcmVudCIsIGxpbmV3aWR0aCA9IC43NSkgKw0KICBtYXBUaGVtZSArIA0KICBsYWJzKHRpdGxlID0gIkF0bGFudGEgMjEgQ291bnR5IE1TQSIpDQpgYGANCg0KDQpgYGB7ciBtc2EgY29udmVyc2lvbiwgaW5jbHVkZT1GQUxTRSwgZXZhbD1GQUxTRX0NCiMjIFNLSVAgIyMNCiNDb252ZXJ0IHNoYXBlZmlsZSB0byBnZW9qc29uIHNvIGNhbiBzdG9yZSBvbiBnaXRodWINCiNzdF93cml0ZShtZXRyb19jb3VudGllcywgYXBwZW5kID0gRkFMU0UsICJDOi9Vc2Vycy9jdG93bi9PbmVEcml2ZSAtIFBlbm5PMzY1L0NsYXNzZXMvQ2xhc3Nlc19TZW00XzIwMjNTcHJpbmcvQ1BMTiA2NzVfTGFuZCBNb2RlbGluZy9Bc3NpZ25tZW50cy9BNV9TcHJhd2xGb3JlY2FzdGluZy9MVUVNX0E1X1JlcG8vRGF0YS9nZW9qc29uL0F0bGFudGFfTWV0cm9fQ291bnRpZXMuZ2VvanNvbiIpDQoNCiNzdF93cml0ZShtZXRyb19jb3VudGllcywgYXBwZW5kID0gRkFMU0UsICJDOi9Vc2Vycy9jdG93bi9PbmVEcml2ZSAtIFBlbm5PMzY1L0NsYXNzZXMvQ2xhc3Nlc19TZW00XzIwMjNTcHJpbmcvQ1BMTiA2NzVfTGFuZCBNb2RlbGluZy9Bc3NpZ25tZW50cy9BNV9TcHJhd2xGb3JlY2FzdGluZy9MVUVNX0E1X1JlcG8vRGF0YS9zaGFwZWZpbGUvQXRsYW50YV9NZXRyb19Cb3VuZGFyeS9BdGxhbnRhX01ldHJvX0NvdW50aWVzLnNocCIpDQoNCiNzdF93cml0ZShtZXRyb19jb3VudGllcywgYXBwZW5kID0gRkFMU0UsICJDOi9Vc2Vycy9jdG93bi9PbmVEcml2ZSAtIFBlbm5PMzY1L0NsYXNzZXMvQ2xhc3Nlc19TZW00XzIwMjNTcHJpbmcvQ1BMTiA2NzVfTGFuZCBNb2RlbGluZy9Bc3NpZ25tZW50cy9BNV9TcHJhd2xGb3JlY2FzdGluZy9MVUVNX0E1X1JlcG8vRGF0YS9nZW9qc29uL0F0bGFudGFNU0FfQm91bmRhcnlfMjFjb3VudGllcy5nZW9qc29uIikNCg0KI3N0X3dyaXRlKG1ldHJvX2NvdW50aWVzLCBhcHBlbmQgPSBGQUxTRSwgIkM6L1VzZXJzL2N0b3duL09uZURyaXZlIC0gUGVubk8zNjUvQ2xhc3Nlcy9DbGFzc2VzX1NlbTRfMjAyM1NwcmluZy9DUExOIDY3NV9MYW5kIE1vZGVsaW5nL0Fzc2lnbm1lbnRzL0E1X1NwcmF3bEZvcmVjYXN0aW5nL0xVRU1fQTVfUmVwby9EYXRhL3NoYXBlZmlsZS9BdGxhbnRhX01ldHJvX0JvdW5kYXJ5XzIxY291bnR5L0F0bGFudGFNU0FfQm91bmRhcnlfMjFjb3VudGllcy5zaHAiKQ0KYGBgDQoNCldlIHRoZW4gY3JlYXRlIGEgZmlzaG5ldCBvZiB0aGUgQXRsYW50YSBNU0EgdXNpbmcgMTAwMCcgeCAxMDAwJyBjZWxscy4gQSBmaXNobmV0IGlzIGEgdmVjdG9yIGRhdGEgc2V0IHRoYXQgd2lsbCBhbGxvdyB1cyB0byBhdHRhY2ggb3RoZXIgaW5mb3JtYXRpb24gdG8gdGhlIGZvcnRoY29taW5nIHJhc3RlciBkYXRhIHNldCBvbiBsYW5kIGNvdmVyLg0KDQpgYGB7ciBmaXNobmV0fQ0KQXRsYW50YU1TQV9maXNobmV0IDwtIA0KICAgIHN0X21ha2VfZ3JpZChtZXRyb19jb3VudGllcywgY2VsbHNpemUgPSAxMDAwLCBzcXVhcmUgPSBUUlVFKSAlPiUNCiAgc3Rfc2YoKSAlPiUgDQogIHN0X3RyYW5zZm9ybSgiRVNSSToxMDIyNjciKQ0KDQpBdGxhbnRhTVNBX2Zpc2huZXQgPC0NCiAgQXRsYW50YU1TQV9maXNobmV0W21ldHJvX2NvdW50aWVzLF0NCg0KI3Bsb3QgTVNBIGJvdW5kYXJ5IHRvIG1ha2Ugc3VyZSBpdHMgcmlnaHQNCmdncGxvdCgpKw0KICBnZW9tX3NmKGRhdGEgPSBBdGxhbnRhTVNBX2Zpc2huZXQsDQogICAgICAgICAgZmlsbCA9ICJsaWdodGdyZXkiKSArDQogIGdlb21fc2YoZGF0YSA9IEF0bGFudGFfTVNBLCANCiAgICAgICAgICBjb2xvciA9ICJibGFjayIsIGZpbGwgPSAidHJhbnNwYXJlbnQiLCBsaW5ld2lkdGggPSAuNzUpICsNCiAgbGFicyh0aXRsZSA9ICJBdGxhbnRhIE1TQSBGaXNobmV0IikgKw0KICBtYXBUaGVtZQ0KYGBgDQoNCiMjIExhbmQgQ292ZXINCg0KIyMjIExhbmQgQ292ZXIgYnkgWWVhciBhbmQgVHlwZQ0KDQpXZSByZXRyaWV2ZSB0aGUgcmFzdGVyIGZpbGVzIG9mIGxhbmQgY292ZXIgZm9yIHRoZSBBdGxhbnRhIE1TQS4gV2UgZG93bmxvYWRlZCB0aGVzZSBkYXRhc2V0cyBmcm9tIHRoZSBNdWx0aS1SZXNvbHV0aW9uIExhbmQgQ2hhcmFjdGVyaXN0aWNzIENvbnNvcnRpdW0ncyBOYXRpb25hbCBMYW5kIENvdmVyIERhdGFiYXNlLCBhbmQgY2xpcHBlZCB0aGVtIGluIEFyY0dJUyB0byBhIHJlY3Rhbmd1bGFyIGJvdW5kYXJ5IGV4dGVudCB0aGF0IGFsaWduZWQgd2l0aCB0aGUgZmFydGhlc3QgcG9pbnRzIG9mIHRoZSBBdGxhbnRhIHJlZ2lvbidzIDIxIGNvdW50eSBib3JkZXIuDQoNCmBgYHtyIEltcG9ydCBmdWxsIG9yaWdpbmFsIExhbmQgQ292ZXIgZGF0YSwgZXZhbD1GQUxTRX0NCiMjU0tJUCMjDQojbG9hZCBmdWxsIHNpemUgcmFzdGVycw0KbGNfMjAwOCA9IHJhc3RlcigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2MtdG93bnNsZXkvTFVFTV9BNV9SZXBvLzU2YmQzNWIzNDNkMzllODhiZDhlMWZkYzg5OTU3ZTZjY2I4MDRmZTkvRGF0YS9yYXN0ZXIvMjAwOExDX3Byb2oudGlmIikNCiAgICANCmxjXzIwMTkgPSByYXN0ZXIoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9jLXRvd25zbGV5L0xVRU1fQTVfUmVwby81NmJkMzViMzQzZDM5ZTg4YmQ4ZTFmZGM4OTk1N2U2Y2NiODA0ZmU5L0RhdGEvcmFzdGVyLzIwMTlMQ19wcm9qLnRpZiIpDQpgYGANCg0KVGhlIHJhc3RlciBmaWxlcyB3ZXJlIHN0aWxsIHZlcnkgbGFyZ2UsIHNvIHdlIGRvd24tc2FtcGxlZCB0aGVtIHRvIG1ha2UgdGhlaXIgc2l6ZSBtb3JlIG1hbmFnZWFibGUgYW5kIHRoZW4gY2xpcHBlZCB0aGVtIHByZWNpc2VseSB0byB0aGUgTVNBIGJvdW5kYXJ5Lg0KDQpgYGB7ciBkb3duc2FtcGxlIHJhc3RlcnMgY3JvcCBhbmQgZXhwb3J0LCBldmFsPUZBTFNFfQ0KIyMgU0tJUCAjIw0KDQojZG93biBzYW1wbGUgdGhlIHJhc3RlciB0byBtYWtlIG1vcmUgbWFuYWdlYWJsZQ0KbGNfMjAwOCA8LSBhZ2dyZWdhdGUobGNfMjAwOCwgZmFjdCA9IDcsIGZ1biA9IG1vZGFsKQ0KbGNfMjAxOSA8LSBhZ2dyZWdhdGUobGNfMjAxOSwgZmFjdCA9IDcsIGZ1biA9IG1vZGFsKQ0KDQojY3JvcCByYXN0ZXJzDQpsY18yMDA4X2Nyb3AgPC0gY3JvcChsY18yMDA4LCBleHRlbnQobWV0cm9fY291bnRpZXMpKQ0KbGNfMjAxOV9jcm9wIDwtIGNyb3AobGNfMjAxOSwgZXh0ZW50KG1ldHJvX2NvdW50aWVzKSkgDQoNCiNtYXNrIHJhc3RlcnMNCmxjXzIwMDhfY3JvcCA8LSBtYXNrKHggPSBsY18yMDA4X2Nyb3AsIG1hc2sgPSBtZXRyb19jb3VudGllcykNCmxjXzIwMTlfY3JvcCA8LSBtYXNrKHggPSBsY18yMDE5X2Nyb3AsIG1hc2sgPSBtZXRyb19jb3VudGllcykNCg0KI0V4cG9ydCBkb3duc2l6ZWQgcmFzdGVycw0KI3dyaXRlUmFzdGVyKGxjXzIwMDhfY3JvcCwgZmlsZW5hbWU9ZmlsZS5wYXRoKHJhc3RfZGlyLCAibGMyMDA4X3NtbC50aWYiKSwgZm9ybWF0PSJHVGlmZiIsIG92ZXJ3cml0ZT1UUlVFKQ0KDQojd3JpdGVSYXN0ZXIobGNfMjAxOV9jcm9wLCBmaWxlbmFtZT1maWxlLnBhdGgoIkM6L1VzZXJzL2N0b3duL09uZURyaXZlIC0gUGVubk8zNjUvQ2xhc3Nlcy9DbGFzc2VzX1NlbTRfMjAyM1NwcmluZy9DUExOIDY3NV9MYW5kIE1vZGVsaW5nL0Fzc2lnbm1lbnRzL0E1X1NwcmF3bEZvcmVjYXN0aW5nL0xVRU1fQTVfUmVwby9EYXRhL3Jhc3RlciIsICJsYzIwMTlfc21sLnRpZiIpLCBmb3JtYXQ9IkdUaWZmIiwgb3ZlcndyaXRlPVRSVUUpDQpgYGANCg0KYGBge3IgaW1wb3J0IGRvd25zYW1wbGVkIHJhc3RlcnN9DQpsY18yMDA4IDwtIHJhc3RlcigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2MtdG93bnNsZXkvTFVFTV9BNV9SZXBvLzI0ZjA4ZTYxYzVlYzk4M2IzZmEzOWYxZjNiMDNmYjkwYWEzYTEzZWUvRGF0YS9yYXN0ZXIvbGMyMDA4X3NtbC50aWYiKSANCg0KbGNfMjAxOSA8LSByYXN0ZXIoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9jLXRvd25zbGV5L0xVRU1fQTVfUmVwby8yNGYwOGU2MWM1ZWM5ODNiM2ZhMzlmMWYzYjAzZmI5MGFhM2ExM2VlL0RhdGEvcmFzdGVyL2xjMjAxOV9zbWwudGlmIikgDQpgYGANCg0KVGhpcyBhbGxvd3MgdXMgdG8gdmlzdWFsaXplIEF0bGFudGEncyBsYW5kIGNvdmVyIGluIDIwMDggYW5kIDIwMTkuDQoNCmBgYHtyIFBsb3QgbGFuZCBjb3ZlciBmb3IgMjAwOCBhbmQgMjAxOSwgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTh9DQpncmlkLmFycmFuZ2UobmNvbCA9IDEsDQpnZ3Bsb3QoKSArDQogIGdlb21fcmFzdGVyKGRhdGE9cmFzdChsY18yMDA4KSAlPiUgbmEub21pdCAlPiUgZmlsdGVyKHZhbHVlID4gMCksIA0KICAgICAgICAgICAgICBhZXMoeCx5LGZpbGw9YXMuZmFjdG9yKHZhbHVlKSkpICsNCiAgc2NhbGVfZmlsbF92aXJpZGlzKGRpc2NyZXRlPVRSVUUsDQogICAgICAgICAgICAgICAgICAgICBuYW1lID0iIiwNCiAgICAgICAgICAgICAgICAgICAgIG9wdGlvbiA9ICJDIikgKw0KICBnZW9tX3NmKGRhdGE9bWV0cm9fY291bnRpZXMsDQogICAgICAgICAgY29sb3IgPSAid2hpdGUiLA0KICAgICAgICAgIGZpbGwgPSAidHJhbnNwYXJlbnQiLA0KICAgICAgICAgIGxpbmV3aWR0aCA9IC41KSArDQogICAgZ2VvbV9zZihkYXRhPUF0bGFudGFfTVNBLA0KICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwNCiAgICAgICAgICBmaWxsID0gInRyYW5zcGFyZW50IiwNCiAgICAgICAgICBsaW5ld2lkdGggPSAuNzUpICsNCiAgbGFicyh0aXRsZSA9ICJMYW5kIENvdmVyLCAyMDA4IikgKw0KICBtYXBUaGVtZSArDQogIHRoZW1lKGxlZ2VuZC5kaXJlY3Rpb249Imhvcml6b250YWwiKSwNCg0KDQpnZ3Bsb3QoKSArDQogIGdlb21fcmFzdGVyKGRhdGE9cmFzdChsY18yMDE5KSAlPiUgbmEub21pdCAlPiUgZmlsdGVyKHZhbHVlID4gMCksIA0KICAgICAgICAgICAgICBhZXMoeCwgeSwgZmlsbD1hcy5mYWN0b3IodmFsdWUpKSkgKw0KICBzY2FsZV9maWxsX3ZpcmlkaXMoZGlzY3JldGU9VFJVRSwNCiAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSIiLA0KICAgICAgICAgICAgICAgICAgICAgb3B0aW9uID0gIkMiKSArDQogIGdlb21fc2YoZGF0YT1tZXRyb19jb3VudGllcywNCiAgICAgICAgICBjb2xvciA9ICJ3aGl0ZSIsDQogICAgICAgICAgZmlsbCA9ICJ0cmFuc3BhcmVudCIsDQogICAgICAgICAgbGluZXdpZHRoID0gLjUpICsNCiAgICAgIGdlb21fc2YoZGF0YT1BdGxhbnRhX01TQSwNCiAgICAgICAgICBjb2xvciA9ICJibGFjayIsDQogICAgICAgICAgZmlsbCA9ICJ0cmFuc3BhcmVudCIsDQogICAgICAgICAgbGluZXdpZHRoID0gLjc1KSArDQogIGxhYnModGl0bGUgPSAiTGFuZCBDb3ZlciwgMjAxOSIpICsNCiAgbWFwVGhlbWUgKw0KICB0aGVtZShsZWdlbmQuZGlyZWN0aW9uPSJob3Jpem9udGFsIikNCikNCmBgYA0KDQpWaWV3aW5nIGxhbmQgY292ZXIgYXQgdGhlIE1TQSBsZXZlbCBpbiAyMDA4IGFuZCAyMDE5IHNob3dzIHRoYXQgY2hhbmdlIGlzIGhhcHBlbmluZyBhdCB0b28gc21hbGwgYSBzY2FsZSB0byBkaXNjZXJuIHZpc3VhbGx5IGF0IHRoaXMgZXh0ZW50LiBUaGVyZWZvcmUsIHdlIHN1bW1vbiB0aGUgcG93ZXJzIG9mIFIuIEZpcnN0LCB3ZSBkaXN0aWxsIHRoZSBtYW55IHNwZWNpZmljIGxhbmQgY292ZXIgY2xhc3NpZmljYXRpb25zIGludG8gc2l4IHNpbXBsZSBidWNrZXRzOiBkZXZlbG9wZWQgbGFuZCwgZm9yZXN0LCBmYXJtbGFuZCwgd2V0bGFuZHMsIHdhdGVyIGJvZGllcywgYW5kIG90aGVyIHVuZGV2ZWxvcGVkIGxhbmRzLg0KDQojIyMgTGFuZCBDb3ZlciBieSBUeXBlDQoNCmBgYHtyIHJlY2xhc3MgTGFuZCBDb3Zlcn0NCmRldmVsb3BlZDA4IDwtIGxjXzIwMDggPT0gMjEgfCBsY18yMDA4ID09IDIyIHwgbGNfMjAwOCA9PSAyMyB8IGxjXzIwMDggPT0gMjQNCmZvcmVzdDA4IDwtIGxjXzIwMDggPT0gNDEgfCBsY18yMDA4ID09IDQyIHwgbGNfMjAwOCA9PSA0MyANCmZhcm0wOCA8LSBsY18yMDA4ID09IDgxIHwgbGNfMjAwOCA9PSA4MiANCndldGxhbmRzMDggPC0gbGNfMjAwOCA9PSA5MCB8IGxjXzIwMDggPT0gOTUgDQpvdGhlclVuZGV2ZWxvcGVkMDggPC0gbGNfMjAwOCA9PSA1MiB8IGxjXzIwMDggPT0gNzEgfCBsY18yMDA4ID09IDMxIA0Kd2F0ZXIwOCA8LSBsY18yMDA4ID09IDExDQoNCmRldmVsb3BlZDE5IDwtIGxjXzIwMTkgPT0gMjEgfCBsY18yMDE5ID09IDIyIHwgbGNfMjAxOSA9PSAyMyB8IGxjXzIwMTkgPT0gMjQNCmZvcmVzdDE5IDwtIGxjXzIwMTkgPT0gNDEgfCBsY18yMDE5ID09IDQyIHwgbGNfMjAxOSA9PSA0MyANCmZhcm0xOSA8LSBsY18yMDE5ID09IDgxIHwgbGNfMjAxOSA9PSA4MiANCndldGxhbmRzMTkgPC0gbGNfMjAxOSA9PSA5MCB8IGxjXzIwMTkgPT0gOTUgDQpvdGhlclVuZGV2ZWxvcGVkMTkgPC0gbGNfMjAxOSA9PSA1MiB8IGxjXzIwMTkgPT0gNzEgfCBsY18yMDE5ID09IDMxIA0Kd2F0ZXIxOSA8LSBsY18yMDE5ID09IDExDQoNCm5hbWVzKGRldmVsb3BlZDA4KSA8LSAiZGV2ZWxvcGVkMDgiDQpuYW1lcyhmb3Jlc3QwOCkgPC0gImZvcmVzdDA4Ig0KbmFtZXMoZmFybTA4KSA8LSAiZmFybTA4Ig0KbmFtZXMod2V0bGFuZHMwOCkgPC0gIndldGxhbmRzMDgiDQpuYW1lcyhvdGhlclVuZGV2ZWxvcGVkMDgpIDwtICJvdGhlclVuZGV2ZWxvcGVkMDgiDQpuYW1lcyh3YXRlcjA4KSA8LSAid2F0ZXIwOCINCg0KbmFtZXMoZGV2ZWxvcGVkMTkpIDwtICJkZXZlbG9wZWQxOSINCm5hbWVzKGZvcmVzdDE5KSA8LSAiZm9yZXN0MTkiDQpuYW1lcyhmYXJtMTkpIDwtICJmYXJtMTkiDQpuYW1lcyh3ZXRsYW5kczE5KSA8LSAid2V0bGFuZHMxOSINCm5hbWVzKG90aGVyVW5kZXZlbG9wZWQxOSkgPC0gIm90aGVyVW5kZXZlbG9wZWQxOSINCm5hbWVzKHdhdGVyMTkpIDwtICJ3YXRlcjE5Ig0KYGBgDQoNClRoaXMgZGlzdGlsbGVkLCBjYXRlZ29yaWNhbCBkYXRhc2V0IGZvciBlYWNoIHllYXIgaXMgdGhlbiBhZ2dyZWdhdGVkIHRvIG91ciBmaXNobmV0IG9mIHRoZSBBdGxhbnRhIE1TQS4gVGhlIHJlc3VsdGluZyBtYXBzIHNob3cgdGhhdCBBdGxhbnRhIGlzIGEgaGVhdmlseSBmb3Jlc3RlZCBhcmVhLiBEZXZlbG9wbWVudCBpcyB2YXN0LCB3aXRoIGl0cyBhbmNob3IgaW4gdGhlIGNlbnRlciBvZiB0aGUgTVNBLCByYWRpYXRpbmcgb3V0d2FyZC4gQSBzaWduaWZpY2FudCBhbW91bnQgb2YgZmFybWxhbmQgZXhpc3RzIGluIGEgdGhpY2sgYmFuZCBhZ2FpbnN0IHRoZSBNU0EncyBwZXJpcGhlcnkuIFVuZGV2ZWxvcGVkIGFyZWFzIGFyZSBzY2F0dGVyZWQsIGFsc28gbW9zdGx5IG5lYXIgdGhlIE1TQSdzIHBlcmlwaGVyeS4NCg0KYGBge3IgdHVybiByYXN0ZXJzIGludG8gZmlzaG5ldCBkYXRhZnJhbWUsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD0xMH0NCg0KdGhlUmFzdGVyTGlzdDA4IDwtIGMoZGV2ZWxvcGVkMDgsIGZvcmVzdDA4LCBmYXJtMDgsIHdldGxhbmRzMDgsIG90aGVyVW5kZXZlbG9wZWQwOCwgd2F0ZXIwOCkNCg0KdGhlUmFzdGVyTGlzdDE5IDwtIGMoZGV2ZWxvcGVkMTksIGZvcmVzdDE5LCBmYXJtMTksIHdldGxhbmRzMTksIG90aGVyVW5kZXZlbG9wZWQxOSwgd2F0ZXIxOSkNCg0KYWdncmVnYXRlZFJhc3RlcnMwOCA8LQ0KICBhZ2dyZWdhdGVSYXN0ZXIodGhlUmFzdGVyTGlzdDA4LCBBdGxhbnRhTVNBX2Zpc2huZXQpICU+JQ0KICBkcGx5cjo6c2VsZWN0KGRldmVsb3BlZDA4LCBmb3Jlc3QwOCwgZmFybTA4LCB3ZXRsYW5kczA4LCBvdGhlclVuZGV2ZWxvcGVkMDgsIHdhdGVyMDgpICU+JQ0KICBtdXRhdGVfaWYoaXMubnVtZXJpYywgYXMuZmFjdG9yKQ0KDQphZ2dyZWdhdGVkUmFzdGVyczE5IDwtDQogIGFnZ3JlZ2F0ZVJhc3Rlcih0aGVSYXN0ZXJMaXN0MTksIEF0bGFudGFNU0FfZmlzaG5ldCkgJT4lDQogIGRwbHlyOjpzZWxlY3QoZGV2ZWxvcGVkMTksIGZvcmVzdDE5LCBmYXJtMTksIHdldGxhbmRzMTksIG90aGVyVW5kZXZlbG9wZWQxOSwgd2F0ZXIxOSkgJT4lDQogIG11dGF0ZV9pZihpcy5udW1lcmljLCBhcy5mYWN0b3IpDQoNCnJhc3RlcnMwOF9tYXAgPC0gYWdncmVnYXRlZFJhc3RlcnMwOCAlPiUNCiAgZ2F0aGVyKHZhcix2YWx1ZSxkZXZlbG9wZWQwODp3YXRlcjA4KSAlPiUNCiAgc3RfY2FzdCgiUE9MWUdPTiIpICU+JSAgICAjanVzdCB0byBtYWtlIHN1cmUgbm8gd2VpcmQgZ2VvbWV0cmllcyBzbGlwcGVkIGluDQogIG11dGF0ZShYID0geHlDKC4pJHgsDQogICAgICAgICBZID0geHlDKC4pJHkpICU+JQ0KICBnZ3Bsb3QoKSArDQogICAgZ2VvbV9zZihkYXRhPW1ldHJvX2NvdW50aWVzKSArDQogICAgZ2VvbV9wb2ludChhZXMoWCxZLCBjb2xvdXI9YXMuZmFjdG9yKHZhbHVlKSkpICsNCiAgICBmYWNldF93cmFwKH52YXIpICsNCiAgICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGMoZ3JheSwgaGlnaGxpZ2h0KSwNCiAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJGYWxzZSIsIlRydWUiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiIikgKw0KICAgIGdlb21fc2YoZGF0YT1tZXRyb19jb3VudGllcywNCiAgICAgICAgICBjb2xvciA9ICJ3aGl0ZSIsDQogICAgICAgICAgZmlsbCA9ICJ0cmFuc3BhcmVudCIsDQogICAgICAgICAgbGluZXdpZHRoID0gLjI1KSArDQogICAgZ2VvbV9zZihkYXRhPUF0bGFudGFfTVNBLA0KICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwNCiAgICAgICAgICBmaWxsID0gInRyYW5zcGFyZW50IiwNCiAgICAgICAgICBsaW5ld2lkdGggPSAuNSkgKw0KICAgIGxhYnModGl0bGUgPSAiTGFuZCBDb3ZlciBUeXBlcywgMjAwOCIsDQogICAgICAgICBzdWJ0aXRsZSA9ICJBcyBmaXNobmV0IGNlbnRyb2lkcyIpICsNCiAgIG1hcFRoZW1lICsNCiAgIHRoZW1lKCkgICsNCiAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCg0KcmFzdGVyczA4X21hcA0KYGBgDQoNCiMjIyBMYW5kIENvdmVyIENoYW5nZQ0KDQpUbyBpZGVudGlmeSBhcmVhcyB3aXRoIGxhbmQgY292ZXIgY2hhbmdlLCB3ZSB1c2UgdGhlIHJhc3RlciBkYXRhIHNldHMgYW5kIHJlY2xhc3NpZnkgdGhlIGxhbmQgY292ZXIgdHlwZXMgYXMgbnVtZXJpY2FsIHZhbHVlcywgd2hlcmUgZGV2ZWxvcGVkIGxhbmRzIGhhdmUgdGhlIHZhbHVlIG9mIDEgYW5kIHVuZGV2ZWxvcGVkIGxhbmRzIGhhdmUgdGhlIHZhbHVlIG9mIDAsIGZvciBlYWNoIG9mIHRoZSB5ZWFycycgZGF0YSBzZXRzLiBXZSBhcmUgdGhlbiBhYmxlIHRvIGFkZCB0aGVzZSBkYXRhIHNldHMgdG9nZXRoZXIgb24gYSBjZWxsLWJ5LWNlbGwgYmFzaXMgdG8gdW5kZXJzdGFuZCB3aGVyZSBkZXZlbG9wbWVudCBoYXMgb2NjdXJyZWQgaW4gMjAxOSBhbmQgd2hlcmUgaXQgd2FzIG5vdCBpbiAyMDA4LiBXaGVuIGFkZGVkIHRvZ2V0aGVyLCB0aGUgY2VsbHMgd2l0aCB2YWx1ZXMgb2YgMSByZXByZXNlbnQgd2hlcmUgbmV3IGRldmVsb3BtZW50IGhhcyBvY2N1cnJlZDsgYSB2YWx1ZSBvZiAyIHNob3dzIHdoZXJlIGRldmVsb3BtZW50IHdhcyBpbiAyMDA4IGFuZCByZW1haW5lZCBpbiAyMDE5LCBhbmQgYSB2YWx1ZSBvZiAwIHNob3dzIGxhbmQgdGhhdCB3YXMgYW5kIHJlbWFpbnMgdW5kZXZlbG9wZWQuIFRoaXMgZnVuY3Rpb24gaXMgbGlrZSB1c2luZyByYXN0ZXIgY2FsY3VsYXRvciBpbiBHSVMuDQoNCmBgYHtyIHJlY2xhc3MgbWF0cml4LCByZXN1bHRzPSdoaWRlJ30NCiNjcmVhdCBtYXRyaXggZm9yIHJlY2xhc3NpZnlpbmcgbGFuZCBjb3Zlcg0KcmVjbGFzc01hdHJpeCA8LSANCiAgbWF0cml4KGMoDQogICAgMCwxMiwwLA0KICAgIDEyLDI0LDEsDQogICAgMjQsSW5mLDApLA0KICBuY29sPTMsIGJ5cm93PVQpDQoNCnJlY2xhc3NNYXRyaXgNCmBgYA0KDQpgYGB7ciBkZXZlbG9wZWQgbGFuZCBjb3ZlciBjaGFuZ2UgcGxvdH0NCiNyZWNsYXNzaWZ5IGxhbmQgY292ZXIgY2hhbmdlIHVzaW5nIHJlY2xhc3MgbWF0cml4DQoNCmxjXzIwMDhyIDwtIA0KICByZWNsYXNzaWZ5KGxjXzIwMDgsIHJlY2xhc3NNYXRyaXgpDQpsY18yMDE5ciA8LSANCiAgcmVjbGFzc2lmeShsY18yMDE5LCByZWNsYXNzTWF0cml4KQ0KDQpsY18yMDA4cltsY18yMDA4ciA8IDFdIDwtIDANCm5hbWVzKGxjXzIwMDhyKSA8LSAiZGV2MDgiDQoNCmxjXzIwMTlyW2xjXzIwMTlyIDwgMV0gPC0gMA0KbmFtZXMobGNfMjAxOXIpIDwtICJkZXYxOSINCg0KZGV2ZWxvcG1lbnRfY2hhbmdlIDwtIGxjXzIwMDhyICsgbGNfMjAxOXINCg0KIyBjb252ZXJ0IHRoZSByYXN0ZXIgdG8gYSB2ZWN0b3INCmRldl9jaGFuZ2VfdmVjIDwtIGFzLnZlY3RvcihkZXZlbG9wbWVudF9jaGFuZ2UpDQoNCiMgY3JlYXRlIHRoZSBoaXN0b2dyYW0gcGxvdCB3aXRoIGdncGxvdDINCmdncGxvdChkYXRhLmZyYW1lKHZhbHVlID0gZGV2X2NoYW5nZV92ZWMpLCBhZXMoeD12YWx1ZSkpICsgDQogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoPS41LCBmaWxsPWhpZ2hsaWdodCwgY29sb3I9IndoaXRlIikgKw0KICBsYWJzKHRpdGxlPSJEZXZlbG9wbWVudCBDaGFuZ2UgYnkgVHlwZSAoMjAwOC0yMDE5KSIsDQogICAgICAgc3VidGl0bGUgPSAiMCA9IFJlbWFpbmVkIFVuZGV2ZWxvcGVkLCAxID0gTmV3IERldmVsb3BtZW50LCAyID0gQWxyZWFkeSBEZXZlbG9wZWQiLA0KICAgICAgIHg9IkRldmVsb3BtZW50IENoYW5nZSIsDQogICAgICAgeT0iRnJlcXVlbmN5IikgKw0KICBwbG90VGhlbWUNCmBgYA0KDQpUaGlzIHNob3dzIHVzIHRoYXQgbW9zdCBjZWxscyBpbiB0aGUgTVNBIHJlbWFpbmVkIHVuZGV2ZWxvcGVkIGZyb20gMjAwOCB0byAyMDE5LCBtYW55IG9mIHRoZW0gd2VyZSBhbHJlYWR5IGRldmVsb3BlZCwgYW5kIGEgZmV3IG9mIHRoZSBwcmV2aW91c2x5IHVuZGV2ZWxvcGVkIGNlbGxzIGNvbnZlcnRlZCB0byBkZXZlbG9wZWQuIFdlIGNhbiB0aGVuIHZpc3VhbGl6ZSB3aGVyZSB0aGUgbmV3IGRldmVsb3BtZW50IGNlbGxzIHdpdGggdGhlIDEgdmFsdWUgYXJlLCB3aGljaCBzaG93cyB0aGF0IG5ldyBkZXZlbG9wbWVudCBoYXMgYmVlbiBzY2F0dGVyZWQgdGhyb3VnaG91dCB0aGUgTVNBLg0KDQpgYGB7ciBwbG90dGluZyBsYW5kIGNvdmVyIGNoYW5nZSwgZmlnLndpZHRoPTh9DQoNCmRldmVsb3BtZW50X2NoYW5nZVtkZXZlbG9wbWVudF9jaGFuZ2UgIT0gMV0gPC0gTkENCg0KZ2dwbG90KCkgKw0KICBnZW9tX3NmKGRhdGE9QXRsYW50YV9NU0EpICsNCiAgZ2VvbV9yYXN0ZXIoZGF0YT1yYXN0KGRldmVsb3BtZW50X2NoYW5nZSkgJT4lIG5hLm9taXQsIA0KICAgICAgICAgICAgICBhZXMoeCx5LGZpbGw9YXMuZmFjdG9yKHZhbHVlKSkpICsNCiAgc2NhbGVfZmlsbF92aXJpZGlzKGRpc2NyZXRlPVRSVUUsDQogICAgICAgICAgICAgICAgICAgICBvcHRpb24gPSAiQyIsDQogICAgICAgICAgICAgICAgICAgICBuYW1lID0iQ2VsbHMgXG53aXRoIExhbmQgQ292ZXJcbkNoYW5nZSIpICsgDQogIGdlb21fc2YoZGF0YT1tZXRyb19jb3VudGllcywNCiAgICAgICAgICBjb2xvciA9ICJ3aGl0ZSIsDQogICAgICAgICAgZmlsbCA9ICJ0cmFuc3BhcmVudCIsDQogICAgICAgICAgbGluZXdpZHRoID0gLjUpICsNCiAgZ2VvbV9zZihkYXRhPUF0bGFudGFfTVNBLA0KICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwNCiAgICAgICAgICBmaWxsID0gInRyYW5zcGFyZW50IiwNCiAgICAgICAgICBsaW5ld2lkdGggPSAuNzUpICsNCiAgbGFicyh0aXRsZT0iRGV2ZWxvcG1lbnQgTGFuZCBDb3ZlciBDaGFuZ2UgKDIwMDgtMjAxOSkiLA0KICAgICAgIHN1YnRpdGxlID0gIkF0bGFudGEgbWV0cm8gYXJlYSB8IFJhc3RlciBjZWxscyIpICsNCiAgbWFwVGhlbWUgKw0KICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KICANCmBgYA0KDQpXZSB0aGVuIGpvaW4gdGhpcyBkYXRhc2V0LCBtYXBwaW5nIG5ldyBkZXZlbG9wbWVudCwgdG8gb3VyIGZpc2huZXQuIFRoaXMgaGFzIHRoZSBlZmZlY3Qgb2YgbWFnbmlmeWluZyB3aGVyZSB0aGUgZGV2ZWxvcG1lbnQgY2hhbmdlIGhhcyBoYXBwZW5lZCBhIGJpdC4NCg0KYGBge3IgbGFuZCBjb3ZlciBkZXZlbG9wbWVudCBjaGFuZ2UsIGZpZy53aWR0aD04fQ0KZGV2X2NoYW5nZSA8LSByZWNsYXNzaWZ5KGRldmVsb3BtZW50X2NoYW5nZSwgcmVjbGFzc01hdHJpeCkNCiAgICAgICAgICAgICAgICAgICAgICAgICANCmRldl9jaGFuZ2VbZGV2ZWxvcG1lbnRfY2hhbmdlIDwgMV0gPC0gTkENCg0KbmFtZXMoZGV2X2NoYW5nZSkgPC0gImRldmVsb3BtZW50X2NoYW5nZSINCg0KDQpjaGFuZ2VQb2ludHMgPC0NCiAgcmFzdGVyVG9Qb2ludHMoZGV2X2NoYW5nZSkgJT4lDQogIGFzLmRhdGEuZnJhbWUoKSAlPiUNCiAgc3RfYXNfc2YoY29vcmRzID0gYygieCIsICJ5IiksIGNycyA9IHN0X2NycyhBdGxhbnRhTVNBX2Zpc2huZXQpKQ0KDQpkZXZfY2hhbmdlX2Zpc2huZXQgPC0gDQogIGFnZ3JlZ2F0ZShjaGFuZ2VQb2ludHMsIEF0bGFudGFNU0FfZmlzaG5ldCwgc3VtKSAlPiUNCiAgbXV0YXRlKGRldmVsb3BtZW50X2NoYW5nZSA9IGlmZWxzZShpcy5uYShkZXZlbG9wbWVudF9jaGFuZ2UpLDAsMSksDQogICAgICAgICBkZXZlbG9wbWVudF9jaGFuZ2UgPSBhcy5mYWN0b3IoZGV2ZWxvcG1lbnRfY2hhbmdlKSkNCg0KZ2dwbG90KCkgKw0KICBnZW9tX3BvaW50KGRhdGE9ZGV2X2NoYW5nZV9maXNobmV0LCANCiAgICAgICAgICAgICBhZXMoeD14eUMoZGV2X2NoYW5nZV9maXNobmV0KSR4LCB5PXh5QyhkZXZfY2hhbmdlX2Zpc2huZXQpJHksIGNvbG91cj1kZXZlbG9wbWVudF9jaGFuZ2UpKSArDQogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gYyhncmF5LCBoaWdobGlnaHQpLA0KICAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJObyBDaGFuZ2UiLCJOZXcgRGV2ZWxvcG1lbnQiKSwNCiAgICAgICAgICAgICAgICAgICAgICBuYW1lID0gIiIpICsNCiAgICBnZW9tX3NmKGRhdGE9bWV0cm9fY291bnRpZXMsDQogICAgICAgICAgY29sb3IgPSAid2hpdGUiLA0KICAgICAgICAgIGZpbGwgPSAidHJhbnNwYXJlbnQiLA0KICAgICAgICAgIGxpbmV3aWR0aCA9IC41KSArDQogICAgZ2VvbV9zZihkYXRhPUF0bGFudGFfTVNBLA0KICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwNCiAgICAgICAgICBmaWxsID0gInRyYW5zcGFyZW50IiwNCiAgICAgICAgICBsaW5ld2lkdGggPSAuNzUpICsNCiAgbGFicyh0aXRsZSA9ICJOZXcgRGV2ZWxvcG1lbnQgKDIwMTkpIiwgc3VidGl0bGUgPSAiQXRsYW50YSBtZXRybyBhcmVhIHwgRmlzaG5ldCBjZW50cm9pZHMiKSArDQogIG1hcFRoZW1lDQoNCiNkZXZfY2hhbmdlX2Zpc2huZXQgPC0gc3Rfd3JpdGUoZGV2X2NoYW5nZV9maXNobmV0LCAiQzovVXNlcnMvTGluZHNleS9EZXNrdG9wL0NQTE42NzVfTGFuZF9Vc2VfTW9kZWxpbmdfRGVza3RvcC9GaW5hbC9MVUVNX0E1X1JlcG8vRGF0YS9nZW9qc29uL2Rldl9jaGFuZ2VfZmlzaG5ldC5nZW9qc29uIikNCg0KYGBgDQoNCiMjIENlbnN1cyBEYXRhOiBQb3B1bGF0aW9uDQoNCk5leHQsIHdlIHNldCB1cCBvdXIgTVNBIHBvcHVsYXRpb24gZGF0YSwgd2hpY2ggd2lsbCBwcmVwYXJlIHVzIGZvciB0aGUgZGVtYW5kLXNpZGUgbW9kZWwuIFRoZSBuZXh0IHR3byBjaHVua3MgZG93bmxvYWQgdGhlIHBvcHVsYXRpb24gZGF0YSBmb3IgMjAwOSBhbmQgMjAxOSBmcm9tIHRoZSBBbWVyaWNhbiBDb21tdW5pdHkgU3VydmV5IDUteWVhciBFc3RpbWF0ZXMgb24gYSBjZW5zdXMgdHJhY3QgbGV2ZWwuDQoNCmBgYHtyIG1ldHJvIHBvcCAyMDA5LCBlY2hvPUZBTFNFLCByZXN1bHRzPSdoaWRlJ30NCiMgTG9vayBoZXJlIGZvciB2YXJpYWJsZSBjb2Rlcw0KYWNzX3ZhcnMgPC0gbG9hZF92YXJpYWJsZXMoMjAwOSwgImFjczUiKQ0KDQojUHVsbCAyMDA5IHBvcHVsYXRpb24NClBvcDA5IDwtIA0KICBnZXRfYWNzKGdlb2dyYXBoeSA9ICJ0cmFjdCIsIHZhcmlhYmxlcyA9ICJCMDEwMDNfMDAxIiwgeWVhciA9IDIwMDksDQogICAgICAgICAgICAgICAgc3RhdGUgPSAxMywgZ2VvbWV0cnkgPSBUUlVFLCANCiAgICAgICAgICAgICAgICBjb3VudHkgPSBDb3VudGllcykgJT4lDQogIHJlbmFtZSgicG9wMDkiID0gZXN0aW1hdGUpICU+JSANCiAgc3RfdHJhbnNmb3JtKHN0X2NycyhBdGxhbnRhTVNBX2Zpc2huZXQpKQ0KYGBgDQoNCmBgYHtyIG1ldHJvIHBvcCAyMDE5LCBlY2hvPUZBTFNFLCByZXN1bHRzPSdoaWRlJ30NCiNQdWxsIDIwMTkgcG9wdWxhdGlvbg0KUG9wMTkgPC0gDQogIGdldF9hY3MoZ2VvZ3JhcGh5ID0gInRyYWN0IiwgdmFyaWFibGVzID0gIkIwMTAwM18wMDEiLCB5ZWFyID0gMjAxOSwNCiAgICAgICAgICAgICAgICBzdGF0ZSA9IDEzLCBnZW9tZXRyeSA9IFRSVUUsIA0KICAgICAgICAgICAgICAgIGNvdW50eSA9IENvdW50aWVzKSAlPiUNCiAgcmVuYW1lKCJwb3AxOSIgPSBlc3RpbWF0ZSkgJT4lIA0KICBzdF90cmFuc2Zvcm0oc3RfY3JzKEF0bGFudGFNU0FfZmlzaG5ldCkpICU+JSANCiAgc3RfYnVmZmVyKC0xKSAjYnVmZmVyIHRoZSB0aGUgdHJhY3RzIGJ5IC0xZnQuIFRoaXMgaXMgZG9uZSBiZWNhdXNlIHRpZHljZW5zdXMgYXBwZWFycyB0byByZXR1cm4gZ2VvbWV0cmllcyB0aGF0IGFyZSBwcm9ibGVtYXRpYyB3aGVuIHN1YmplY3RlZCB0byB0aGUgYXJlYSB3ZWlnaHRlZCBpbnRlcnBvbGF0aW9uIGZ1bmN0aW9uDQpgYGANCg0KV2UgdGhlbiB2aXN1YWxpemUgdGhlIHBvcHVsYXRpb24gZm9yIHRoZSB0d28geWVhcnMgYnkgdGhlIHJlc3BlY3RpdmUgY2Vuc3VzIHRyYWN0IGJvdW5kYXJpZXMgZm9yIGVhY2ggb2YgdGhlIHllYXJzLg0KDQpgYGB7ciBwbG90IGJvdGggcG9wIHllYXJzLCBmaWcud2lkdGg9MTJ9DQpncmlkLmFycmFuZ2UoDQpnZ3Bsb3QoKSArDQogIGdlb21fc2YoZGF0YSA9IFBvcDA5LCBhZXMoZmlsbD1mYWN0b3IobnRpbGUocG9wMDksNSkpKSwgY29sb3VyPU5BKSArDQogIGdlb21fc2YoZGF0YSA9IEF0bGFudGFfTVNBLCANCiAgICAgICAgICBjb2xvciA9ICJibGFjayIsIGZpbGwgPSAidHJhbnNwYXJlbnQiLCBsaW5ld2lkdGggPSAuNzUpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTUsDQogICAgICAgICAgICAgICAgICAgIGxhYmVscz1xdWludGlsZUJyZWFrcyhQb3AwOSwicG9wMDkiKSwNCiAgICAgICAgICAgICAgICAgICBuYW1lPSJRdWludGlsZVxuQnJlYWtzIikgKw0KICBsYWJzKHRpdGxlPSIyMDA5IFBvcHVsYXRpb25cbkF0bGFudGEgMjEtQ291bnR5IE1TQSIsDQogICAgICAgc3VidGl0bGU9IkJ5IENlbnN1cyBUcmFjdCIpICsNCiAgbWFwVGhlbWUsDQoNCmdncGxvdCgpICsNCiAgZ2VvbV9zZihkYXRhID0gUG9wMTksIGFlcyhmaWxsPWZhY3RvcihudGlsZShwb3AxOSw1KSkpLCBjb2xvdXI9TkEpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTUsDQogICAgICAgICAgICAgICAgICAgIGxhYmVscz1xdWludGlsZUJyZWFrcyhQb3AxOSwicG9wMTkiKSwNCiAgICAgICAgICAgICAgICAgICAgbmFtZT0iUXVpbnRpbGVcbkJyZWFrcyIpICsNCiAgZ2VvbV9zZihkYXRhID0gQXRsYW50YV9NU0EsIA0KICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwgZmlsbCA9ICJ0cmFuc3BhcmVudCIsIGxpbmV3aWR0aCA9IC43NSkgKw0KICBsYWJzKHRpdGxlPSIyMDE5IFBvcHVsYXRpb25cbkF0bGFudGEgMjEtQ291bnR5IE1TQSIsDQogICAgICAgc3VidGl0bGU9IkJ5IENlbnN1cyBUcmFjdCIpICsNCiAgbWFwVGhlbWUsIG5jb2w9MikNCmBgYA0KDQpXZSB0aGVuIHJlY29uY2lsZSB0aGUgdHJhY3QgYm91bmRhcmllcyB3aXRoIHRoZSBmaXNobmV0IGdyaWQgY2VsbHMgdG8gYmUgYWJsZSB0byBjb25kdWN0IGFuYWx5c2lzIGFuZCBidWlsZCBtb2RlbHMgd2l0aCBvdXIgZmlzaG5ldCBkYXRhIHNldC4NCg0KYGBge3IgcmVjb25jaWxlIHRyYWN0cyBhbmQgZmlzaG5ldCBjZWxsc30NCiMjIyBCcmVha2luZyBoZXJlIG9uIHNlbGVjdChmaXNobmV0SUQpDQpBdGxhbnRhTVNBX2Zpc2huZXQgPC0NCiAgQXRsYW50YU1TQV9maXNobmV0ICU+JQ0KICByb3duYW1lc190b19jb2x1bW4oImZpc2huZXRJRCIpICU+JSANCiAgbXV0YXRlKGZpc2huZXRJRCA9IGFzLm51bWVyaWMoZmlzaG5ldElEKSkgJT4lDQogIGRwbHlyOjpzZWxlY3QoZmlzaG5ldElEKQ0KDQpmaXNobmV0UG9wMDkgPC0NCiAgc3RfaW50ZXJwb2xhdGVfYXcoUG9wMDlbInBvcDA5Il0sIEF0bGFudGFNU0FfZmlzaG5ldCwgZXh0ZW5zaXZlPVRSVUUpICU+JQ0KICBhcy5kYXRhLmZyYW1lKC4pICU+JQ0KICByb3duYW1lc190b19jb2x1bW4odmFyID0gImZpc2huZXRJRCIpICU+JQ0KICBsZWZ0X2pvaW4oQXRsYW50YU1TQV9maXNobmV0ICU+JQ0KICAgICAgICAgICAgICBtdXRhdGUoZmlzaG5ldElEID0gYXMuY2hhcmFjdGVyKGZpc2huZXRJRCkpLA0KICAgICAgICAgICAgLiwgYnk9YygiZmlzaG5ldElEIj0nZmlzaG5ldElEJykpICU+JSANCiAgbXV0YXRlKHBvcDA5ID0gcmVwbGFjZV9uYShwb3AwOSwwKSkgJT4lDQogIGRwbHlyOjpzZWxlY3QocG9wMDkpDQoNCmZpc2huZXRQb3AxOSA8LQ0KICBzdF9pbnRlcnBvbGF0ZV9hdyhQb3AxOVsicG9wMTkiXSwgQXRsYW50YU1TQV9maXNobmV0LCBleHRlbnNpdmU9VFJVRSkgJT4lDQogIGFzLmRhdGEuZnJhbWUoLikgJT4lDQogIHJvd25hbWVzX3RvX2NvbHVtbih2YXIgPSAiZmlzaG5ldElEIikgJT4lDQogIGxlZnRfam9pbihBdGxhbnRhTVNBX2Zpc2huZXQgJT4lDQogICAgICAgICAgICAgIG11dGF0ZShmaXNobmV0SUQgPSBhcy5jaGFyYWN0ZXIoZmlzaG5ldElEKSksDQogICAgICAgICAgICAuLCBieT1jKCJmaXNobmV0SUQiPSdmaXNobmV0SUQnKSkgJT4lIA0KICBtdXRhdGUocG9wMTkgPSByZXBsYWNlX25hKHBvcDE5LDApKSAlPiUNCiAgZHBseXI6OnNlbGVjdChwb3AxOSkNCg0KZmlzaG5ldFBvcCA8LSANCiAgY2JpbmQoZmlzaG5ldFBvcDA5LCBmaXNobmV0UG9wMTkpICU+JQ0KICBkcGx5cjo6c2VsZWN0KHBvcDA5LHBvcDE5KSAlPiUNCiAgbXV0YXRlKHBvcF9DaGFuZ2UgPSBwb3AxOSAtIHBvcDA5KQ0KYGBgDQoNCldlIGNhbiB0aGVuIGNvbXBhcmUgcG9wdWxhdGlvbiBieSB0cmFjdCB2cyBwb3B1bGF0aW9uIGJ5IGdyaWQgY2VsbC4NCg0KYGBge3IgcGxvdCBwb3AgdHJhY3QgYW5kIGdyaWQgY2VsbCwgZmlnLndpZHRoPTEyfQ0KZ3JpZC5hcnJhbmdlKA0KZ2dwbG90KCkgKw0KICBnZW9tX3NmKGRhdGE9UG9wMTksIGFlcyhmaWxsPWZhY3RvcihudGlsZShwb3AxOSw1KSkpLGNvbG91cj1OQSkgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlNSwNCiAgICAgICAgICAgICAgICAgICAgbGFiZWxzPXN1YnN0cihxdWludGlsZUJyZWFrcyhQb3AxOSwicG9wMTkiKSwxLDQpLA0KICAgICAgICAgICAgICAgICAgIG5hbWU9IlF1aW50aWxlXG5CcmVha3MiKSArDQogIGdlb21fc2YoZGF0YSA9IEF0bGFudGFfTVNBLCANCiAgICAgICAgICBjb2xvciA9ICJibGFjayIsIGZpbGwgPSAidHJhbnNwYXJlbnQiLCBsaW5ld2lkdGggPSAuNzUpICsNCiAgbGFicyh0aXRsZT0iMjAxOSBQb3B1bGF0aW9uXG5BdGxhbnRhIDIxLUNvdW50eSBNU0EiLA0KICAgICAgIHN1YnRpdGxlPSJSZXByZXNlbnRlZCBhcyB0cmFjdHM7IEJvdW5kYXJpZXMgb21pdHRlZCIpICsNCiAgbWFwVGhlbWUsDQoNCmdncGxvdCgpICsNCiAgZ2VvbV9zZihkYXRhPWZpc2huZXRQb3AsIGFlcyhmaWxsPWZhY3RvcihudGlsZShwb3AxOSw1KSkpLCBjb2xvdXI9TkEpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTUsDQogICAgICAgICAgICAgICAgICAgbGFiZWxzPXN1YnN0cihxdWludGlsZUJyZWFrcyhmaXNobmV0UG9wLCJwb3AxOSIpLDEsNCksDQogICAgICAgICAgICAgICAgICAgbmFtZT0iUXVpbnRpbGVcbkJyZWFrcyIpICsNCiAgZ2VvbV9zZihkYXRhID0gQXRsYW50YV9NU0EsIA0KICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwgZmlsbCA9ICJ0cmFuc3BhcmVudCIsIGxpbmV3aWR0aCA9IC43NSkgKw0KICBsYWJzKHRpdGxlPSIyMDE5IFBvcHVsYXRpb25cbkF0bGFudGEgMjEtQ291bnR5IE1TQSIsDQogICAgICAgc3VidGl0bGU9IlJlcHJlc2VudGVkIGFzIGZpc2huZXQgZ3JpZGNlbGxzOyBCb3VuZGFyaWVzIG9taXR0ZWQiKSArDQogIG1hcFRoZW1lLCBuY29sPTIpDQpgYGANCg0KIyMgSGlnaHdheSBEaXN0YW5jZQ0KDQpOZXh0LCB3ZSBwcmVwYXJlIG91ciBzdXBwbHktc2lkZSBtb2RlbCBieSBsb2FkaW5nIGluIHNwYXRpYWwgZGF0YSBmb3IgQXRsYW50YSdzIGV4aXN0aW5nIGhpZ2h3YXlzLg0KDQpgYGB7ciBoaWdod2F5IHRvIGdlb2pzb24sIGluY2x1ZGU9RkFMU0UsIGV2YWw9RkFMU0V9DQojU2tpcA0KI21ldHJvX2hpZ2h3YXlzIDwtIHN0X3JlYWQoIkM6L1VzZXJzL2N0b3duL09uZURyaXZlIC0gUGVubk8zNjUvQ2xhc3Nlcy9DbGFzc2VzX1NlbTRfMjAyM1NwcmluZy9DUExOIDY3NV9MYW5kIE1vZGVsaW5nL0Fzc2lnbm1lbnRzL0E1X1NwcmF3bEZvcmVjYXN0aW5nL0xVRU1fQTVfUmVwby9EYXRhL3NoYXBlZmlsZS9BdGxhbnRhTWV0cm9fTWFqb3JfUm9hZHMvTWFqb3JfUm9hZHMuc2hwIikNCg0KI3N0X3dyaXRlKG1ldHJvX2hpZ2h3YXlzLCBhcHBlbmQgPSBGQUxTRSwgIkM6L1VzZXJzL2N0b3duL09uZURyaXZlIC0gUGVubk8zNjUvQ2xhc3Nlcy9DbGFzc2VzX1NlbTRfMjAyM1NwcmluZy9DUExOIDY3NV9MYW5kIE1vZGVsaW5nL0Fzc2lnbm1lbnRzL0E1X1NwcmF3bEZvcmVjYXN0aW5nL0xVRU1fQTVfUmVwby9EYXRhL2dlb2pzb24vTWFqb3JfUm9hZHMuZ2VvanNvbiIpDQpgYGANCg0KYGBge3IgaGlnaHdheXMsIGZpZy53aWR0aD04LCBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQ0KQXRsYW50YUhpZ2h3YXlzIDwtDQogIHN0X3JlYWQoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9jLXRvd25zbGV5L0xVRU1fQTVfUmVwby84YjY4OTY1MTI4NmJiNGZmMjg1NTQwOTViZDM5Y2UxYmQ4NzY1NWU0L0RhdGEvZ2VvanNvbi9NYWpvcl9Sb2Fkcy5nZW9qc29uIikgJT4lDQogIHN0X3RyYW5zZm9ybShzdF9jcnMobWV0cm9fY291bnRpZXMpKSAlPiUNCiAgc3RfaW50ZXJzZWN0aW9uKG1ldHJvX2NvdW50aWVzKSAlPiUgDQogIGZpbHRlcihGRUFUVVJFX1RZID09ICJTdGF0ZSBIaWdod2F5IikNCg0KZW1wdHlSYXN0ZXIgPC0gZGV2ZWxvcG1lbnRfY2hhbmdlDQplbXB0eVJhc3RlcltdIDwtIE5BDQoNCmdncGxvdCgpICsNCiAgZ2VvbV9wb2ludChkYXRhPWRldl9jaGFuZ2VfZmlzaG5ldCwgDQogICAgICAgICAgICAgYWVzKHg9eHlDKGRldl9jaGFuZ2VfZmlzaG5ldCkkeCwgeT14eUMoZGV2X2NoYW5nZV9maXNobmV0KSR5LCBjb2xvdXI9ZGV2ZWxvcG1lbnRfY2hhbmdlKSkgKw0KICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGMoZ3JheSwgIiNFRDc5NTNGRiIpLA0KICAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJObyBDaGFuZ2UiLCJOZXcgRGV2ZWxvcG1lbnQiKSwNCiAgICAgICAgICAgICAgICAgICAgICBuYW1lID0gIiIpICsNCiAgZ2VvbV9zZihkYXRhPUF0bGFudGFIaWdod2F5cywgY29sb3IgPSBoaWdobGlnaHQsIGxpbmV3aWR0aCA9IC41KSArDQogIGdlb21fc2YoZGF0YSA9IEF0bGFudGFfTVNBLCBmaWxsID0gInRyYW5zcGFyZW50IiwNCiAgICAgICAgICBjb2xvciA9ICJibGFjayIsIGxpbmV3aWR0aCA9IC43NSkgKw0KICBsYWJzKHRpdGxlID0gIkhpZ2h3YXlzIGFuZCBOZXcgRGV2ZWxvcG1lbnQiKSArDQogIG1hcFRoZW1lDQpgYGANCg0KVGhpcyBzdWdnZXN0cyB0aGVyZSBtYXkgYmUgYW4gYXNzb2NpYXRpb24gYmV0d2VlbiBwcm94aW1pdHkgdG8gaGlnaHdheXMgYW5kIG5ldyBkZXZlbG9wbWVudC4gVG8gYmV0dGVyIHVuZGVyc3RhbmQgdGhpcyByZWxhdGlvbnNoaXAsIHdlIGNhbGN1bGF0ZSBob3cgZmFyIGVhY2ggZmlzaG5ldCBjZWxsIGlzIGZyb20gYSBoaWdod2F5LiBXZSBkbyB0aGlzIGJ5IGNvbnZlcnRpbmcgdGhlIGhpZ2h3YXkgbGF5ZXIgdG8gcmFzdGVyLCBjb252ZXJ0aW5nIHRoZSByYXN0ZXIgdG8gcG9pbnRzLCBhbmQgdGhlbiBjYWxjdWxhdGluZyB0aGUgbWVhbiBkaXN0YW5jZSBmcm9tIGEgaGlnaHdheSBmb3IgZWFjaCBncmlkIGNlbGwuDQoNCmBgYHtyIGNvbnZlcnQgaGlnaHdheXMgdG8gcmFzdGVyLCBldmFsPUZBTFNFfQ0KI1NLSVAjDQojY3JlYXRlIGEgcmFzdGVyIGxheWVyIG9mIGhpZ2h3YXlzDQpoaWdod2F5X3Jhc3RlciA8LSANCiAgYXMoQXRsYW50YUhpZ2h3YXlzLCdTcGF0aWFsJykgJT4lDQogIHJhc3Rlcml6ZSguLGVtcHR5UmFzdGVyKQ0KDQojRXhwb3J0IHJhc3RlcnMNCg0KcmFzdF9kaXIgPSBwYXN0ZShkaXJfY3QsICdyYXN0ZXInLCBzZXA9Jy8nKQ0KDQojd3JpdGVSYXN0ZXIoaGlnaHdheV9yYXN0ZXIsIGZpbGVuYW1lPWZpbGUucGF0aChyYXN0X2RpciwgImhpZ2h3YXlfcmFzdGVyLnRpZiIpLCBmb3JtYXQ9IkdUaWZmIiwgb3ZlcndyaXRlPVRSVUUpDQpgYGANCg0KVGhpcyB2aXN1YWxpemVzIGVhY2ggY2VsbCdzIGRpc3RhbmNlIGZyb20gYSBoaWdod2F5Lg0KDQpgYGB7ciBkaXN0IGZyb20gaGlnaHdheXMsIGVjaG89RkFMU0UsIGZpZy53aWR0aD04fQ0KI2ltcG9ydCBoaWdod2F5IHJhc3Rlcg0KaGlnaHdheV9yYXN0ZXIgPC0gcmFzdGVyKHBhc3RlKGRpcl9naXQsICJhNWE1YjBjOTA5OWI5N2Y2OGMwZDFhZWFkY2RmZTlkYmQyOTYwYTlhL0RhdGEvcmFzdGVyL2hpZ2h3YXlfcmFzdGVyLnRpZiIsIHNlcD0nLycpKQ0KDQpoaWdod2F5X3Jhc3Rlcl9kaXN0YW5jZSA8LSBkaXN0YW5jZShoaWdod2F5X3Jhc3RlcikNCm5hbWVzKGhpZ2h3YXlfcmFzdGVyX2Rpc3RhbmNlKSA8LSAiZGlzdGFuY2VfaGlnaHdheXMiDQoNCiNFeHBvcnQgaGlnaHdheSByYXN0ZXIgZGlzdGFuY2UgdGhhbiBpbXBvcnQgZmlsZSBmcm9tIGdpdGh1YiB0byBzcGVlZCBwcm9jZXNzaW5nDQojaGlnaHdheV9yYXN0ZXIgPC0gcmFzdGVyKHBhc3RlKGRpcl9naXQsICJhNWE1YjBjOTA5OWI5N2Y2OGMwZDFhZWFkY2RmZTlkYmQyOTYwYTlhL0RhdGEvcmFzdGVyL2hpZ2h3YXlfcmFzdGVyLnRpZiIsIHNlcD0nLycpKQ0KDQpoaWdod2F5UG9pbnRzIDwtDQogIHJhc3RlclRvUG9pbnRzKGhpZ2h3YXlfcmFzdGVyX2Rpc3RhbmNlKSAlPiUNCiAgYXMuZGF0YS5mcmFtZSgpICU+JQ0KICBzdF9hc19zZihjb29yZHMgPSBjKCJ4IiwgInkiKSwgY3JzID0gc3RfY3JzKEF0bGFudGFNU0FfZmlzaG5ldCkpDQoNCmhpZ2h3YXlQb2ludHNfZmlzaG5ldCA8LSANCiAgYWdncmVnYXRlKGhpZ2h3YXlQb2ludHMsIEF0bGFudGFNU0FfZmlzaG5ldCwgbWVhbikgJT4lDQogIG11dGF0ZShkaXN0YW5jZV9oaWdod2F5cyA9IGlmZWxzZShpcy5uYShkaXN0YW5jZV9oaWdod2F5cyksMCxkaXN0YW5jZV9oaWdod2F5cykpDQoNCmdncGxvdCgpICsNCiAgZ2VvbV9zZihkYXRhPW1ldHJvX2NvdW50aWVzKSArDQogIGdlb21fcG9pbnQoZGF0YT1oaWdod2F5UG9pbnRzX2Zpc2huZXQsIGFlcyh4PXh5QyhoaWdod2F5UG9pbnRzX2Zpc2huZXQpWywxXSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5PXh5QyhoaWdod2F5UG9pbnRzX2Zpc2huZXQpWywyXSwgDQogICAgICAgICAgICAgICAgIGNvbG91cj1mYWN0b3IobnRpbGUoZGlzdGFuY2VfaGlnaHdheXMsNSkpKSxzaXplPTEuNSkgKw0KICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGU1LA0KICAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1zdWJzdHIocXVpbnRpbGVCcmVha3MoaGlnaHdheVBvaW50c19maXNobmV0LCJkaXN0YW5jZV9oaWdod2F5cyIpLDEsOCksDQogICAgICAgICAgICAgICAgICAgICAgbmFtZT0iUXVpbnRpbGVcbkJyZWFrcyIpICsNCiAgZ2VvbV9zZihkYXRhPUF0bGFudGFIaWdod2F5cywgY29sb3VyID0gImJsYWNrIikgKw0KICBnZW9tX3NmKGRhdGE9QXRsYW50YV9NU0EsDQogICAgICAgICAgY29sb3IgPSAiYmxhY2siLA0KICAgICAgICAgIGZpbGwgPSAidHJhbnNwYXJlbnQiLA0KICAgICAgICAgIGxpbmV3aWR0aCA9IC43NSkgKw0KICBsYWJzKHRpdGxlID0gIkRpc3RhbmNlIHRvIEhpZ2h3YXlzIiwNCiAgICAgICBzdWJ0aXRsZSA9ICJJbiBGZWV0OyBIaWdod2F5cyB2aXN1YWxpemVkIGluIGJsYWNrIikgKw0KICBtYXBUaGVtZQ0KYGBgDQoNCiMjIENhbGN1bGF0aW5nIHRoZSBTcGF0aWFsIExhZyBvZiBEZXZlbG9wbWVudA0KDQpXZSBtZWFzdXJlIGFjY2Vzc2liaWxpdHkgYnkgd2F5IG9mIGEgc3BhdGlhbCBsYWcsIGh5cG90aGVzaXppbmcgdGhhdCBuZXcgZGV2ZWxvcG1lbnQgaXMgYSBmdW5jdGlvbiBvZiBkaXN0YW5jZSB0byBleGlzdGluZyBkZXZlbG9wbWVudC4gVGhlIHNob3J0ZXIgdGhlIGRpc3RhbmNlLCB0aGUgbW9yZSBhY2Nlc3NpYmxlIGEgZ3JpZCBjZWxsIGlzIHRvIGV4aXN0aW5nIGRldmVsb3BtZW50LiBXZSBtZWFzdXJlIHRoaXMgYnkgY2FsY3VsYXRpbmcgdGhlIGF2ZXJhZ2UgZGlzdGFuY2UgZnJvbSBlYWNoIGdyaWQgY2VsbCB0byBpdHMgMiBuZWFyZXN0IGRldmVsb3BlZCBuZWlnaGJvcmluZyBncmlkIGNlbGxzIGluIDIwMDkuDQoNCmBgYHtyIG5uIGZ1bmN0aW9ufQ0Kbm5fZnVuY3Rpb24gPC0gZnVuY3Rpb24obWVhc3VyZUZyb20sbWVhc3VyZVRvLGspIHsNCiAgI2NvbnZlcnQgdGhlIHNmIGxheWVycyB0byBtYXRyaWNlcw0KICBtZWFzdXJlRnJvbV9NYXRyaXggPC0NCiAgICBhcy5tYXRyaXgobWVhc3VyZUZyb20pDQogIG1lYXN1cmVUb19NYXRyaXggPC0NCiAgICBhcy5tYXRyaXgobWVhc3VyZVRvKQ0KICBubiA8LSAgIA0KICAgIGdldC5rbm54KG1lYXN1cmVUbywgbWVhc3VyZUZyb20sIGspJG5uLmRpc3QNCiAgICBvdXRwdXQgPC0NCiAgICBhcy5kYXRhLmZyYW1lKG5uKSAlPiUNCiAgICByb3duYW1lc190b19jb2x1bW4odmFyID0gInRoaXNQb2ludCIpICU+JQ0KICAgIGdhdGhlcihwb2ludHMsIHBvaW50X2Rpc3RhbmNlLCBWMTpuY29sKC4pKSAlPiUNCiAgICBhcnJhbmdlKGFzLm51bWVyaWModGhpc1BvaW50KSkgJT4lDQogICAgZ3JvdXBfYnkodGhpc1BvaW50KSAlPiUNCiAgICBzdW1tYXJpemUocG9pbnREaXN0YW5jZSA9IG1lYW4ocG9pbnRfZGlzdGFuY2UpKSAlPiUNCiAgICBhcnJhbmdlKGFzLm51bWVyaWModGhpc1BvaW50KSkgJT4lIA0KICAgIGRwbHlyOjpzZWxlY3QoLXRoaXNQb2ludCkgJT4lDQogICAgcHVsbCgpDQogIA0KICByZXR1cm4ob3V0cHV0KSAgDQp9DQpgYGANCg0KYGBge3IgZGV2ZWxvcG1lbnQgc3BhdGlhbCBsYWcsIGZpZy53aWR0aD04fQ0KQXRsYW50YU1TQV9maXNobmV0JGxhZ0RldmVsb3BtZW50MDggPC0NCiAgICBubl9mdW5jdGlvbih4eUMoQXRsYW50YU1TQV9maXNobmV0KSwNCiAgICAgICAgICAgICAgICB4eUMoZmlsdGVyKGFnZ3JlZ2F0ZWRSYXN0ZXJzMDgsIGRldmVsb3BlZDA4PT0xKSksDQogICAgICAgICAgICAgICAgMikNCg0KIyBjb21wdXRlIG5hdHVyYWwgYnJlYWtzIGZvciAibGFnRGV2ZWxvcG1lbnQwOCINCmJyZWFrcyA8LSBjbGFzc0ludGVydmFscyhBdGxhbnRhTVNBX2Zpc2huZXQkbGFnRGV2ZWxvcG1lbnQwOCwgbj00LCBzdHlsZT0iamVua3MiKQ0KDQojUGxvdA0KZ2dwbG90KCkgKw0KICBnZW9tX3NmKGRhdGE9bWV0cm9fY291bnRpZXMpICsNCiAgZ2VvbV9wb2ludChkYXRhPUF0bGFudGFNU0FfZmlzaG5ldCwgDQogICAgICAgICAgICAgYWVzKHg9eHlDKEF0bGFudGFNU0FfZmlzaG5ldClbLDFdLCB5PXh5QyhBdGxhbnRhTVNBX2Zpc2huZXQpWywyXSwgDQogICAgICAgICAgICAgICAgIGNvbG91cj1jdXQobGFnRGV2ZWxvcG1lbnQwOCwgYnJlYWtzJGJya3MpKSwgc2l6ZT0xLjUpICsNCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXM9cGFsZXR0ZTUsDQogICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWZvcm1hdChicmVha3MkYnJrcywgbnNtYWxsPTEpLA0KICAgICAgICAgICAgICAgICAgICAgIG5hbWU9IkRpc3RhbmNlIChmdCkiKSArDQogIGdlb21fc2YoZGF0YT1tZXRyb19jb3VudGllcywNCiAgICAgICAgICBjb2xvciA9ICJ3aGl0ZSIsDQogICAgICAgICAgZmlsbCA9ICJ0cmFuc3BhcmVudCIsDQogICAgICAgICAgbGluZXdpZHRoID0gLjUpICsNCiAgZ2VvbV9zZihkYXRhPUF0bGFudGFfTVNBLA0KICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwNCiAgICAgICAgICBmaWxsID0gInRyYW5zcGFyZW50IiwNCiAgICAgICAgICBsaW5ld2lkdGggPSAuNzUpICsNCiAgbGFicyh0aXRsZSA9ICJTcGF0aWFsIExhZyB0byAyMDA4IERldmVsb3BtZW50IiwNCiAgICAgICBzdWJ0aXRsZSA9ICJBcyBmaXNobmV0IGNlbnRyb2lkcyIpICsNCiAgbWFwVGhlbWUNCmBgYA0KDQpUaGlzIHNob3dzIG1vc3Qgb2YgdGhlIGFyZWEgaW4gdGhlIE1TQSdzIGlubmVyIGNvdW50aWVzIHdhcyBuZWFyIGV4aXN0aW5nIGRldmVsb3BtZW50IGluIDIwMDguIEl0IHdhcyBvbmx5IHRoZSBjb3VudGllcyBhdCB0aGUgb3V0ZXIgZnJpbmdlcyB0aGF0IHN0aWxsIGhhZCBhIGZldyBhcmVhcyByZWxhdGl2ZWx5IGRpc3RhbnQgZnJvbSBkZXZlbG9wbWVudC4NCg0KIyMgQ3JlYXRlIEZpbmFsIERhdGFzZXQNCg0KV2UgdGhlbiBhZ2dyZWdhdGUgYWxsIHRoZXNlIGRhdGEgdG8gYSBmaW5hbCBmaXNobmV0IGRhdGEgc2V0IGZvciBmZWVkaW5nIGludG8gb3VyIG1vZGVsLiBUaGlzIGZpc2huZXQgaW5jbHVkZXMgY29sdW1ucyB3aGljaCBpbmRpY2F0ZSwgY2VsbC1ieS1jZWxsLCBpZiB0aGVyZSBoYXMgYmVlbiBkZXZlbG9wbWVudCBjaGFuZ2UsIGxhbmQgdXNlIHR5cGUsIHRoZSBjZWxsJ3MgZGlzdGFuY2UgdG8gaGlnaHdheXMsIHRoZSBjZWxsJ3MgZGlzdGFuY2UgdG8gZXhpc3RpbmcgZGV2ZWxvcG1lbnQsIGFuZCBpdHMgcG9wdWxhdGlvbiBpbiBib3RoIHllYXJzLg0KDQpgYGB7ciBjb21iaW5lIGFsbCB2YXJpYWJsZXMgaW50byBvbmUgZGF0YXNldH0NCmRhdCA8LSAgDQogIGNiaW5kKA0KICAgIEF0bGFudGFNU0FfZmlzaG5ldCwgZGV2X2NoYW5nZV9maXNobmV0LCBoaWdod2F5UG9pbnRzX2Zpc2huZXQsIGZpc2huZXRQb3AsIGFnZ3JlZ2F0ZWRSYXN0ZXJzMDgpICU+JQ0KICBkcGx5cjo6c2VsZWN0KGRldmVsb3BtZW50X2NoYW5nZSwgZGV2ZWxvcGVkMDgsIGZvcmVzdDA4LCBmYXJtMDgsIHdldGxhbmRzMDgsIG90aGVyVW5kZXZlbG9wZWQwOCwgd2F0ZXIwOCwNCiAgICAgICAgICAgICAgICBwb3AwOSwgcG9wMTksIHBvcF9DaGFuZ2UsIGRpc3RhbmNlX2hpZ2h3YXlzLCBsYWdEZXZlbG9wbWVudDA4KSAlPiUNCiAgc3Rfam9pbihtZXRyb19jb3VudGllcykgJT4lDQogIG11dGF0ZShkZXZlbG9wZWQxOSA9IGlmZWxzZShkZXZlbG9wbWVudF9jaGFuZ2UgID09IDEgJiBkZXZlbG9wZWQwOCA9PSAxLCAwLCAxKSkgJT4lICNjaGFybGllIHRvIGNvbmZpcm0gaWYgdGhlIGVsc2UgdmFsdWUgc2hvdWxkIGJlIDEgb3IgJ2RldmVsb3BlZCcNCiAgZmlsdGVyKHdhdGVyMDggPT0gMCkgDQpgYGANCg0KIyBEYXRhIEV4cGxvcmF0aW9uDQoNCk5vdyB0aGF0IGFsbCBvZiBvdXIgZGF0YSBhcmUgYXNzZW1ibGVkLCB3ZSBleHBsb3JlIGhvdyBlYWNoIGZhY3RvciB1bmNvdmVyZWQgdGh1cyBmYXIgaXMgcmVsYXRlZCB0byBuZXcgZGV2ZWxvcG1lbnQuIFRoaXMgcGxvdCBkaXNwbGF5cyBkaXN0YW5jZSB0byBoaWdod2F5cyBhbmQgdGhlIHNwYXRpYWwgbGFnIG9mIGRldmVsb3BtZW50IGFzIGNvbnRpbnVvdXMgdmFyaWFibGVzLiBUaGlzIHNob3dzIHVzIHRoYXQgbmV3IGRldmVsb3BtZW50IGhhcyBiZWVuLCBvbiBhdmVyYWdlLCBjbG9zZXIgdG8gaGlnaHdheXMsIGFuZCBjbG9zZXIgdG8gZXhpc3RpbmcgZGV2ZWxvcG1lbnQgaW4gYWdncmVnYXRlLCB0aGFuIGNlbGxzIHRoYXQgaGFkIG5vIGNoYW5nZSBpbiBsYW5kIGNvdmVyLiBUaGlzIHN1cHBvcnRzIG91ciBwcmV2aW91cyBoeXBvdGhlc2lzIHRoYXQgbmV3IGRldmVsb3BtZW50IG1pZ2h0IGJlIG9jY3VycmluZyBjbG9zZXIgdG8gaGlnaHdheXMgYW5kIHRvIHByZXZpb3VzbHkgZGV2ZWxvcGVkIGFyZWFzLg0KDQpgYGB7ciBwbG90dGluZyB0aGUgYXZlcmFnZXMgKG1lYW5zKSBvZiBpbmZyYXN0cnVjdHVyZS9kZXZlbG9wbWVudH0NCg0KZGF0ICU+JQ0KICBkcGx5cjo6c2VsZWN0KGRpc3RhbmNlX2hpZ2h3YXlzLGxhZ0RldmVsb3BtZW50MDgsZGV2ZWxvcG1lbnRfY2hhbmdlKSAlPiUgDQogIGdhdGhlcihWYXJpYWJsZSwgVmFsdWUsIC1kZXZlbG9wbWVudF9jaGFuZ2UsIC1nZW9tZXRyeSkgJT4lDQogIGdncGxvdCguLCBhZXMoZGV2ZWxvcG1lbnRfY2hhbmdlLCBWYWx1ZSwgZmlsbD1kZXZlbG9wbWVudF9jaGFuZ2UpKSArIA0KICAgIGdlb21fYmFyKHBvc2l0aW9uID0gImRvZGdlIiwgc3RhdCA9ICJzdW1tYXJ5IiwgZnVuLnkgPSAibWVhbiIpICsNCiAgICBmYWNldF93cmFwKH5WYXJpYWJsZSkgKw0KICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGUyLA0KICAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJObyBDaGFuZ2UiLCJOZXcgRGV2ZWxvcG1lbnQiKSwNCiAgICAgICAgICAgICAgICAgICAgICBuYW1lPSIiKSArDQogICAgbGFicyh0aXRsZT0iTmV3IERldmVsb3BtZW50IGFzIGEgRnVuY3Rpb24gb2YgdGhlIENvbnRpbnVvdXMgVmFyaWFibGVzIikgKw0KICAgIHBsb3RUaGVtZQ0KDQpgYGANCg0KVGhpcyBuZXh0IHBsb3Qgc2hvd3MgdGhhdCBuZXcgZGV2ZWxvcG1lbnQgaGFzLCBvbiBhdmVyYWdlLCBvY2N1cnJlZCBpbiBjZWxscyB3aGVyZSB0aGUgcG9wdWxhdGlvbiBpcyBncmVhdGVyIG9yIGdyb3dpbmcuIEZyb20gdGhpcywgd2UgbGVhcm4gdGhhdCBkZXZlbG9wbWVudCBpbiBBdGxhbnRhIE1TQSBpcyBhbHNvIHBvc2l0aXZlbHkgY29ycmVsYXRlZCB3aXRoIHRoZSBhbW91bnQgb2YgcG9wdWxhdGlvbiBhbmQgcG9wdWxhdGlvbiBncm93dGggaW4gYW4gYXJlYS4NCg0KYGBge3IgYmFyIHBsb3RzIG9mIHBvcHVsYXRpb24gdmFyaWFibGVzfQ0KDQpkYXQgJT4lDQogIGRwbHlyOjpzZWxlY3QocG9wMDkscG9wMTkscG9wX0NoYW5nZSxkZXZlbG9wbWVudF9jaGFuZ2UpICU+JQ0KICBnYXRoZXIoVmFyaWFibGUsIFZhbHVlLCAtZGV2ZWxvcG1lbnRfY2hhbmdlLCAtZ2VvbWV0cnkpICU+JQ0KICBnZ3Bsb3QoLiwgYWVzKGRldmVsb3BtZW50X2NoYW5nZSwgVmFsdWUsIGZpbGw9ZGV2ZWxvcG1lbnRfY2hhbmdlKSkgKyANCiAgICBnZW9tX2Jhcihwb3NpdGlvbiA9ICJkb2RnZSIsIHN0YXQgPSAic3VtbWFyeSIsIGZ1bi55ID0gIm1lYW4iKSArDQogICAgZmFjZXRfd3JhcCh+VmFyaWFibGUpICsNCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlMiwNCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiTm8gQ2hhbmdlIiwiTmV3IERldmVsb3BtZW50IiksDQogICAgICAgICAgICAgICAgICAgICAgbmFtZT0iIikgKw0KICAgIGxhYnModGl0bGU9Ik5ldyBEZXZlbG9wbWVudCBhcyBhIEZ1bmN0aW9uIG9mIEZhY3RvciBWYXJpYWJsZXMiKSArDQogICAgcGxvdFRoZW1lDQoNCmBgYA0KDQpGcm9tIHRoZSBhYm92ZSBkYXRhIGV4cGxvcmF0aW9uLCB3ZSBjYW4gc2VlIHRoYXQgZGV2ZWxvcG1lbnQgaXMgcG9zaXRpdmVseSBjb3JyZWxhdGVkIHdpdGggcHJveGltaXR5IHRvIGhpZ2h3YXlzIGFuZCBkZXZlbG9wbWVudCwgYXMgd2VsbCBhcyB3aXRoIGV4aXN0aW5nIGFuZCBncm93aW5nIHBvcHVsYXRpb24uIEhvd2V2ZXIsIHRoaXMgbGVhdmVzIHVzIHdvbmRlcmluZyB3aGljaCB0eXBlcyBvZiBsYW5kIGNvdmVyIGFyZSBtb3N0IGxpa2VseSB0byBjb252ZXJ0IHRvIGRldmVsb3BlZCBsYW5kPw0KDQpUaGUgY29udmVyc2lvbiB0YWJsZSBzaG93cyB1cyB0aGF0IGZvcmVzdGVkIGFyZWFzIGluIDIwMDggd2VyZSB0aGUgbW9zdCBsaWtlbHkgdG8gaGF2ZSBiZWVuIGRldmVsb3BlZCBieSAyMDE5LiBUaGlzIHNob3dzIHRoYXQgQXRsYW50YSBoYXMgYSBwcmVmZXJlbmNlIGZvciBkZXZlbG9waW5nIHdvb2RsYW5kcyBmaXJzdCwgd2l0aCBhcmVhcyBhZGphY2VudCB0byB0aG9zZSB0aGF0IGFyZSBhbHJlYWR5IGRldmVsb3BlZCBhcyBhIGNsb3NlIHNlY29uZC4NCg0KYGBge3IgbGFuZCBjb3ZlciBjb252ZXJzaW9uIHRhYmxlcywgZmlnLndpZHRoPTYsIGZpZy5oZWlnaHQ9Nn0NCmRhdCAlPiUNCiAgZHBseXI6OnNlbGVjdChkZXZlbG9wbWVudF9jaGFuZ2U6b3RoZXJVbmRldmVsb3BlZDA4LGRldmVsb3BlZDA4KSAlPiUNCiAgZ2F0aGVyKExhbmRfQ292ZXJfVHlwZSwgVmFsdWUsIC1kZXZlbG9wbWVudF9jaGFuZ2UsIC1nZW9tZXRyeSkgJT4lDQogICBzdF9zZXRfZ2VvbWV0cnkoTlVMTCkgJT4lDQogICAgIGdyb3VwX2J5KGRldmVsb3BtZW50X2NoYW5nZSwgTGFuZF9Db3Zlcl9UeXBlKSAlPiUNCiAgICAgc3VtbWFyaXplKG4gPSBzdW0oYXMubnVtZXJpYyhWYWx1ZSkpKSAlPiUNCiAgICAgdW5ncm91cCgpICU+JQ0KICAgIG11dGF0ZShDb252ZXJzaW9uX1JhdGUgPSBwYXN0ZTAocm91bmQoMTAwICogbi9zdW0obiksIDIpLCAiJSIpKSAlPiUNCiAgICBmaWx0ZXIoZGV2ZWxvcG1lbnRfY2hhbmdlID09IDEpICU+JQ0KICBkcGx5cjo6c2VsZWN0KExhbmRfQ292ZXJfVHlwZSxDb252ZXJzaW9uX1JhdGUpICU+JQ0KICByZWFjdGFibGUodGhlbWUgPSByZWFjdGFibGVUaGVtZShzdHlsZSA9IGxpc3QoZm9udEZhbWlseSA9ICJBcmlhbCwgc2Fucy1zZXJpZiIsIGZvbnRTaXplID0gIjEuMHJlbSIpKSkNCmBgYA0KDQo8YnI+DQoNCiMgQmluYXJ5IExvZ2lzdGljIFJlZ3Jlc3Npb24gLSBQcmVkaWN0aW5nIGZvciAyMDE5DQoNCk5vdyB0aGF0IHdlIGhhdmUgZ2F0aGVyZWQgYSBudW1iZXIgb2YgZmFjdG9ycyB3ZSBoeXBvdGhlc2l6ZWQgdG8gYmUgcmVsYXRlZCB0byBuZXcgZGV2ZWxvcG1lbnQsIGFuZCBoYXZlIHByb3ZlZCB0aGF0IHRoZXkgaGF2ZSBhdCBsZWFzdCBzb21lIGFzc29jaWF0aW9uIHdpdGggZGV2ZWxvcG1lbnQsIHdlIGNhbiBidWlsZCBhbmQgdHJhaW4gYSBtb2RlbCB0byBwcmVkaWN0IG5ldyBkZXZlbG9wbWVudCBiYXNlZCBvbiB0aGVtLg0KDQpXZSBjaG9zZSB0byB1c2UgYmlub21pYWwgbG9naXN0aWMgcmVncmVzc2lvbiBmb3IgdGhpcyBhc3NpZ25tZW50IGJlY2F1c2UgaXQgaXMgYSB0eXBlIG9mIHN0YXRpc3RpY2FsIG1vZGVsIHRoYXQgaXMgd2VsbCBzdWl0ZWQgdG8gYW5hbHl6aW5nIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiBhIGJpbmFyeSByZXNwb25zZSB2YXJpYWJsZSBhbmQgb25lIG9yIG1vcmUgcHJlZGljdG9yIHZhcmlhYmxlcy4gSXQgZXN0aW1hdGVzIHRoZSBwcm9iYWJpbGl0eSBvZiB0aGUgcmVzcG9uc2UgdmFyaWFibGUgdGFraW5nIG9uZSBvZiB0d28gcG9zc2libGUgdmFsdWVzIChpbiB0aGlzIGNhc2UgZGV2ZWxvcGVkIG9yIG5vdCBkZXZlbG9wZWQpIGJhc2VkIG9uIHRoZSB2YWx1ZXMgb2YgdGhlIHByZWRpY3RvciB2YXJpYWJsZXMuDQoNClRvIHByZWRpY3QgdGhlIGxpa2VsaWhvb2Qgb2YgZnV0dXJlIGRldmVsb3BtZW50LCB3ZSB1c2UgZGF0YSBmcm9tIDIwMDgvMjAwOSB0byBjcmVhdGUgYSBzZXJpZXMgb2YgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbHMgdG8gcHJlZGljdCBsYW5kIHVzZSBjaGFuZ2UgaW4gMjAxOS4gV2UgdGhlbiBldmFsdWF0ZSB0aGUgcGVyZm9ybWFuY2Ugb2YgdGhlc2UgbW9kZWxzIHRvIHNlbGVjdCBvdXIgYmVzdCBtb2RlbCBmb3IgcHJlZGljdGluZyBkZXZlbG9wbWVudCBpbiAyMDI5Lg0KDQojIyBNb2RlbCBCdWlsZGluZw0KDQpUaGUgZmlyc3Qgc3RlcCBvZiB0aGlzIHByb2Nlc3MgaXMgcmFuZG9tbHkgc3BsaXR0aW5nIHRoZSBkYXRhIGluIGhhbGYgdG8gY3JlYXRlIGEgdGVzdGluZyBkYXRhIHNldCB3aXRoIG9uZSBoYWxmIGFuZCBhIHRyYWluaW5nIGRhdGEgc2V0IHdpdGggdGhlIG90aGVyLg0KDQpgYGB7ciB0cmFpbmluZyBzZXRzLCByZXN1bHRzPSdoaWRlJ30NCnNldC5zZWVkKDM0NTYpDQp0cmFpbkluZGV4IDwtIA0KICBjcmVhdGVEYXRhUGFydGl0aW9uKGRhdCRkZXZlbG9wZWQwOCwgcCA9IC41MCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaXN0ID0gRkFMU0UsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGltZXMgPSAxKQ0KZGF0VHJhaW4gPC0gZGF0WyB0cmFpbkluZGV4LF0NCmRhdFRlc3QgIDwtIGRhdFstdHJhaW5JbmRleCxdDQoNCmBgYA0KDQpXZSB0aGVuIGJ1aWxkIGEgc2VyaWVzIG9mIGJpbmFyeSBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVscyBiYXNlZCBvbiBvdXIgaW5kZXBlbmRlbnQgdmFyaWFibGVzLg0KDQpUaGUgc2l4IG1vZGVscyB3ZSBjcmVhdGVkIGFyZSBudW1iZXJlZCB0byByZWZsZWN0IGluY3JlYXNpbmcgY29tcGxleGl0eS4gV2hpbGUgTW9kZWwgMSBjb25jZW50cmF0ZXMgZXhjbHVzaXZlbHkgb24gbGFuZCBjb3ZlciB0eXBlIGFuZCB3aGV0aGVyIGRldmVsb3BtZW50IGNoYW5nZSBvY2N1cnJlZCBiZXR3ZWVuIHRoZSB0d28gdGltZSBwZXJpb2RzLCBsYXRlciBtb2RlbHMgaW5jb3Jwb3JhdGUgdGhlIHNwYXRpYWwgbGFnIG9mIGRldmVsb3BtZW50LCBwb3B1bGF0aW9uIGVsZW1lbnRzLCBhbmQgb3VyIGRpc3RhbmNlIHZhcmlhYmxlcy4NCg0KYGBge3IgbGluZWFyIG1vZGVscyBhbmQgcGxvdHRpbmcgdGhlaXIgUiBzcWF1cmVkIHZhbHVlcyBhbmQgdGVzdCBzZXQgaGlzdG9ncmFtIHBsb3R9DQpNb2RlbDEgPC0gZ2xtKGRldmVsb3BtZW50X2NoYW5nZSB+IHdldGxhbmRzMDggKyBmb3Jlc3QwOCAgKyBmYXJtMDggKyBvdGhlclVuZGV2ZWxvcGVkMDgsIA0KICAgICAgICAgICAgICBmYW1pbHk9ImJpbm9taWFsIihsaW5rPSJsb2dpdCIpLCBkYXRhID0gZGF0VHJhaW4pDQoNCk1vZGVsMiA8LSBnbG0oZGV2ZWxvcG1lbnRfY2hhbmdlIH4gd2V0bGFuZHMwOCArIGZvcmVzdDA4ICArIGZhcm0wOCArIG90aGVyVW5kZXZlbG9wZWQwOCArIGxhZ0RldmVsb3BtZW50MDgsIA0KICAgICAgICAgICAgICBmYW1pbHk9ImJpbm9taWFsIihsaW5rPSJsb2dpdCIpLCBkYXRhID0gZGF0VHJhaW4pDQogICAgICAgICAgICAgIA0KTW9kZWwzIDwtIGdsbShkZXZlbG9wbWVudF9jaGFuZ2UgfiB3ZXRsYW5kczA4ICsgZm9yZXN0MDggICsgZmFybTA4ICsgb3RoZXJVbmRldmVsb3BlZDA4ICsgbGFnRGV2ZWxvcG1lbnQwOCArIHBvcDA5LCANCiAgICAgICAgICAgICAgZmFtaWx5PSJiaW5vbWlhbCIobGluaz0ibG9naXQiKSwgZGF0YSA9IGRhdFRyYWluKSAgICAgICAgICANCiAgICAgICAgICAgICAgDQpNb2RlbDQgPC0gZ2xtKGRldmVsb3BtZW50X2NoYW5nZSB+IHdldGxhbmRzMDggKyBmb3Jlc3QwOCAgKyBmYXJtMDggKyBvdGhlclVuZGV2ZWxvcGVkMDggKyBsYWdEZXZlbG9wbWVudDA4ICsgcG9wMDkgKyBwb3AxOSwgDQogICAgICAgICAgICAgIGZhbWlseT0iYmlub21pYWwiKGxpbms9ImxvZ2l0IiksIGRhdGEgPSBkYXRUcmFpbikgICAgICAgICAgICAgIA0KICAgICAgICAgICAgDQpNb2RlbDUgPC0gZ2xtKGRldmVsb3BtZW50X2NoYW5nZSB+IHdldGxhbmRzMDggKyBmb3Jlc3QwOCAgKyBmYXJtMDggKyBvdGhlclVuZGV2ZWxvcGVkMDggKyBsYWdEZXZlbG9wbWVudDA4ICsgcG9wMDkgKyBwb3AxOSArIHBvcF9DaGFuZ2UsDQogICAgICAgICAgICAgIGZhbWlseT0iYmlub21pYWwiKGxpbms9ImxvZ2l0IiksIGRhdGEgPSBkYXRUcmFpbikgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICANCk1vZGVsNiA8LSBnbG0oZGV2ZWxvcG1lbnRfY2hhbmdlIH4gd2V0bGFuZHMwOCArIGZvcmVzdDA4ICArIGZhcm0wOCArIG90aGVyVW5kZXZlbG9wZWQwOCArIGxhZ0RldmVsb3BtZW50MDggKyBwb3BfQ2hhbmdlICsgcG9wMDkgKyBwb3AxOSArIGRpc3RhbmNlX2hpZ2h3YXlzLCANCiAgICAgICAgICAgICAgZmFtaWx5PSJiaW5vbWlhbCIobGluaz0ibG9naXQiKSwgZGF0YSA9IGRhdFRyYWluKSANCg0KDQptb2RlbExpc3QgPC0gcGFzdGUwKCJNb2RlbCIsIDE6NikNCg0KbWFwX2RmYyhtb2RlbExpc3QsIGZ1bmN0aW9uKHgpcFIyKGdldCh4KSkpWzQsXSAlPiUNCiAgc2V0TmFtZXMocGFzdGUwKCJNb2RlbCIsMTo2KSkgJT4lDQogIGdhdGhlcihNb2RlbCxNY0ZhZGRlbikgJT4lDQogIGdncGxvdChhZXMoTW9kZWwsTWNGYWRkZW4pKSArDQogICAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiLCBmaWxsID0gaGlnaGxpZ2h0KSArDQogICAgbGFicyh0aXRsZT0gIk1jRmFkZGVuIFItU3F1YXJlZCBieSBNb2RlbCIpICsNCiAgICBwbG90VGhlbWUNCg0KYGBgDQoNCldlIHRoZW4gZXhhbWluZSB0aGUgTWNGYWRkZW4gUi1TcXVhcmVkIG9mIHRoZSBzaXggbW9kZWxzIHRvIHNlbGVjdCBvdXIgYmVzdCBmaW5hbCBtb2RlbC4gTW9kZWwgMSBjbGVhcmx5IHBlcmZvcm1zIG1vcmUgcG9vcmx5IHRoYW4gdGhlIG90aGVycy4gSG93ZXZlciwgbW9kZWxzIDItNCBhbHNvIHBlcmZvcm0gbW9kZXJhdGVseSBsZXNzIHdlbGwgdGhhbiB0aGUgcmVtYWluaW5nIG1vZGVscy4gVGhlcmVmb3JlLCB3ZSBlbGltaW5hdGUgbW9kZWxzIDEtNCBhbmQgY2hvb3NlIE1vZGVsIDYgYXMgb3VyIGZpbmFsIG1vZGVsIGJlY2F1c2UgaXQgcGVyZm9ybXMgdGhlIGJlc3QgYW5kIGFsbG93cyB1cyB0byBleHBsb3JlIHRoZSBlZmZlY3RzIG9mIGFsbCBvZiBvdXIgdmFyaWFibGVzIG9mIGludGVyZXN0Lg0KDQpXZSB0aGVuIHVzZSB0aGUgcHJlZGljdGVkIHZhbHVlcyBmcm9tIE1vZGVsIDYgdG8gY3JlYXRlIGEgaGlzdG9ncmFtIG9mIHRoZSBtb2RlbCdzIHRlc3Qgc2V0IHByZWRpY3RlZCBwcm9iYWJpbGl0aWVzLiBUaGlzIHNob3dzIHVzIHRoYXQgYSBoaWdoIGRlbnNpdHkgb2YgY2VsbHMgaGF2ZSByb3VnaGx5IGEgNDAlIGxpa2VsaWhvb2Qgb2YgZGV2ZWxvcG1lbnQuIFRoaXMgbWFrZXMgc2Vuc2UgZ2l2ZW4gdGhlIHNwcmF3bGluZyBuYXR1cmUgb2YgdGhlIEF0bGFudGEgTVNBJ3MgZXhpc3RpbmcgZGV2ZWxvcG1lbnQgcGF0dGVybnMsIHdoaWNoIGFyZSBhY3JlYWdlIGludGVuc2l2ZS4gSXQgYWxzbyBtYWtlcyBzZW5zZSBnaXZlbiB0aGUgbWFwIG9mIG5ldyBkZXZlbG9wbWVudCB3ZSBwbG90dGVkIGVhcmxpZXIsIGluIHdoaWNoIGl0IHNlZW1zIGZlYXNpYmxlIHRoYXQgcm91Z2hseSAvfjMwLTQwJSBvZiB0aGUgTVNBIHdhcyBjb3ZlcmVkIGJ5IG5ldyBkZXZlbG9wbWVudCBpbiAyMDE5Lg0KDQpgYGB7ciBEZW5zaXR5IHBsb3Qgb2YgcHJvYmFiaWxpdGllcyBvYnNlcnZlZCBieSBjbGFzc30NCnRlc3RTZXRQcm9icyA8LSANCiAgZGF0YS5mcmFtZShjbGFzcyA9IGRhdFRlc3QkZGV2ZWxvcG1lbnRfY2hhbmdlLA0KICAgICAgICAgICAgIHByb2JzID0gcHJlZGljdChNb2RlbDYsIGRhdFRlc3QsIHR5cGU9InJlc3BvbnNlIikpIA0KDQpjaGFydF90ZXN0UHJvYnMgPC0gIA0KZ2dwbG90KHRlc3RTZXRQcm9icywgYWVzKHByb2JzKSkgKw0KICBnZW9tX2RlbnNpdHkoYWVzKGZpbGw9Y2xhc3MpLCBhbHBoYT0wLjc1KSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGUyLA0KICAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiTm8gQ2hhbmdlIiwiTmV3IERldmVsb3BtZW50IikpICsNCiAgbGFicyh0aXRsZSA9ICJIaXN0b2dyYW0gb2YgdGVzdCBzZXQgcHJlZGljdGVkIHByb2JhYmlsaXRpZXMiLA0KICAgICAgIHg9IlByZWRpY3RlZCBQcm9iYWJpbGl0aWVzIix5PSJEZW5zaXR5IikgKw0KICBwbG90VGhlbWUNCg0KY2hhcnRfdGVzdFByb2JzDQpgYGANCg0KIyMgQWNjdXJhY3kNCg0KVGhpcyBuZXh0IHNlZ21lbnQgdGVzdHMgb3VyIG1vZGVsJ3M6DQoNCjEpICBTZW5zaXRpdml0eTogdGhlIGV4dGVudCB0byB3aGljaCBpdCBhY2N1cmF0ZWx5IHByZWRpY3RzIG5ldyBkZXZlbG9wbWVudCwgb3IgInRydWUgcG9zaXRpdmVzIiwgYW5kOg0KMikgIFNwZWNpZmljaXR5LCB0aGUgZXh0ZW50IHRvIHdoaWNoIGl0IGFjY3VyYXRlbHkgcHJlZGljdHMgInRydWUgbmVnYXRpdmVzLiINCg0KT3VyIHVsdGltYXRlIGdvYWwgd2l0aCBhIGxhbmQgY292ZXIgY2hhbmdlIG1vZGVsIGlzIHRvIG9wdGltaXplIHRoZSBiYWxhbmNlIGJldHdlZW4gdGhlIG1vZGVsJ3MgcHJlZGljdGlvbnMgd2l0aCByZWdhcmQgdG8gYm90aCBzZW5zaXRpdml0eSBhbmQgc3BlY2lmaWNpdHkuIE1lYW5pbmcsIHdlIHdhbnQgdGhlIG1vZGVsIHRvIGJlIGNhcGFibGUgb2YgcHJlZGljdGluZyB3aGVyZSBkZXZlbG9wbWVudCBvY2N1cnMgc28gdGhhdCB3ZSBjYW4gcGxhbiBmb3IgdGhvc2UgYXJlYXMsIGJ1dCB3ZSBhbHNvIHdhbnQgaXQgdG8gYWNjdXJhdGVseSBwcmVkaWN0IHdoZXJlIGRldmVsb3BtZW50IHdvdWxkIG5vdCBvY2N1ciwgdG8gaW5mb3JtIG91ciBvdGhlciBsYW5kIHVzZSBwbGFubmluZy4NCg0KQXQgdGhlIGZpcnN0IHR3byB0aHJlc2hvbGRzIHNwZWNpZmllZCBiZWxvdywgNSUgYW5kIDE3JSwgd2UgY2FuIHNlZSB0aGF0IHRoZSA1JSB0aHJlc2hvbGQgdmVyeSBhY2N1cmF0ZWx5IHByZWRpY3RzIHRoZSAidHJ1ZSBuZWdhdGl2ZXMsIiB3aGVyZSBkZXZlbG9wbWVudCBkb2VzIG5vdCBvY2N1ci4gVGhlIHRocmVzaG9sZCBvZiAxNyUsIGhvd2V2ZXIsIHBlcmZvcm1zIGJldHRlciBvbiB0aGUgInRydWUgcG9zaXRpdmUiIHJhdGUgb2YgcHJlZGljdGVkIHYuIGFjdHVhbCBkZXZlbG9wbWVudCwgdGhhdCBoYXMgb2NjdXJyZWQuIFdlIGZvdW5kLCBob3dldmVyLCB0aGF0IGEgdGhyZXNob2xkIG9mIGFyb3VuZCAzNCUgZ2l2ZXMgdGhlIGJlc3QgYmFsYW5jZSBiZXR3ZWVuIHRoZSB0cmFkZS1vZmZzIG9mIHNlbnNpdGl2aXR5IGFuZCBzcGVjaWZpY2l0eS4gV2UgY2hvc2UgMzQlIGJlY2F1c2UgaXQgZ2l2ZXMgdXMgdGhlIGhpZ2hlc3Qgb3ZlcmFsbCBhY2N1cmFjeSwgYW5kIGFsbG93cyB1cyB0byBrZWVwIGJvdGggb3VyIHNlbnNpdGl2aXR5IGFuZCBzcGVjaWZpY2l0eSBvdmVyIDUwJS4NCg0KYGBge3IgdGVzdGluZyBtb2RlbCBzZW5zaXRpdml0eSwgZmlnLmhlaWdodD02fQ0Kb3B0aW9ucyh5YXJkc3RpY2suZXZlbnRfZmlyc3QgPSBGQUxTRSkNCg0KdGVzdFNldFByb2JzIDwtIA0KICB0ZXN0U2V0UHJvYnMgJT4lIA0KICBtdXRhdGUocHJlZENsYXNzXzA1ID0gYXMuZmFjdG9yKGlmZWxzZSh0ZXN0U2V0UHJvYnMkcHJvYnMgPj0gMC4wNSAsMSwwKSksDQogICAgICAgICBwcmVkQ2xhc3NfMTcgPSBhcy5mYWN0b3IoaWZlbHNlKHRlc3RTZXRQcm9icyRwcm9icyA+PSAwLjE3ICwxLDApKSwNCiAgICAgICAgIHByZWRDbGFzc18zNCA9IGFzLmZhY3RvcihpZmVsc2UodGVzdFNldFByb2JzJHByb2JzID49IDAuMzQgLDEsMCkpKSANCg0KdGVzdFNldFByb2JzICU+JQ0KICBkcGx5cjo6c2VsZWN0KC1wcm9icykgJT4lDQogIGdhdGhlcihWYXJpYWJsZSwgVmFsdWUsIC1jbGFzcykgJT4lDQogIGdyb3VwX2J5KFZhcmlhYmxlKSAlPiUNCiAgc3VtbWFyaXplKFNlbnNpdGl2aXR5ID0gcm91bmQoeWFyZHN0aWNrOjpzZW5zX3ZlYyhjbGFzcyxmYWN0b3IoVmFsdWUpKSwyKSwNCiAgICAgICAgICAgIFNwZWNpZmljaXR5ID0gcm91bmQoeWFyZHN0aWNrOjpzcGVjX3ZlYyhjbGFzcyxmYWN0b3IoVmFsdWUpKSwyKSwNCiAgICAgICAgICAgIEFjY3VyYWN5ID0gcm91bmQoeWFyZHN0aWNrOjphY2N1cmFjeV92ZWMoY2xhc3MsZmFjdG9yKFZhbHVlKSksMikpICU+JSANCiAgcmVhY3RhYmxlKHRoZW1lID0gcmVhY3RhYmxlVGhlbWUoc3R5bGUgPSBsaXN0KGZvbnRGYW1pbHkgPSAiQXJpYWwsIHNhbnMtc2VyaWYiLCBmb250U2l6ZSA9ICIxLjByZW0iKSkpDQoNCmBgYA0KDQpXZSB0aGVuIGNvbnZlcnQgdGhlc2UgdHJ1ZS9mYWxzZSBwb3NpdGl2ZS9uZWdhdGl2ZSBpbmRpY2F0b3JzIHRvIGZhY3RvcnMgaW4gb3JkZXIgdG8gbWFwIHRoZW0uDQoNCmBgYHtyIGRldmVsb3BtZW50IHByZWRpY3Rpb25zIGJhc2VkIG9uIHRocmVzaG9sZHMsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD0xMH0NCnByZWRzRm9yTWFwIDwtICAgICAgICAgDQogIGRhdCAlPiUNCiAgICBtdXRhdGUocHJvYnMgPSBwcmVkaWN0KE1vZGVsNiwgZGF0LCB0eXBlPSJyZXNwb25zZSIpICwNCiAgICAgICAgICAgVGhyZXNob2xkXzVfUGN0ID0gYXMuZmFjdG9yKGlmZWxzZShwcm9icyA+PSAwLjA1ICwxLDApKSwNCiAgICAgICAgICAgVGhyZXNob2xkXzE3X1BjdCA9ICBhcy5mYWN0b3IoaWZlbHNlKHByb2JzID49IDAuMTcgLDEsMCkpLA0KICAgICAgICAgICBUaHJlc2hvbGRfMzRfUGN0ID0gIGFzLmZhY3RvcihpZmVsc2UocHJvYnMgPj0gMC4zNCAsMSwwKSkpICU+JQ0KICAgIGRwbHlyOjpzZWxlY3QoZGV2ZWxvcG1lbnRfY2hhbmdlLFRocmVzaG9sZF81X1BjdCxUaHJlc2hvbGRfMTdfUGN0LCBUaHJlc2hvbGRfMzRfUGN0KSAlPiUNCiAgICBnYXRoZXIoVmFyaWFibGUsVmFsdWUsIC1nZW9tZXRyeSkgJT4lDQogICAgc3RfY2FzdCgiUE9MWUdPTiIpDQoNCmdncGxvdCgpICsNCiAgZ2VvbV9wb2ludChkYXRhPXByZWRzRm9yTWFwLCBhZXMoeD14eUMocHJlZHNGb3JNYXApWywxXSwgeT14eUMocHJlZHNGb3JNYXApWywyXSwgY29sb3VyPVZhbHVlKSkgKw0KICBmYWNldF93cmFwKH5WYXJpYWJsZSkgKw0KICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGMoZ3JheSwgaGlnaGxpZ2h0KSwgbGFiZWxzPWMoIk5vIENoYW5nZSIsIk5ldyBEZXZlbG9wbWVudCIpLA0KICAgICAgICAgICAgICAgICAgICAgIG5hbWU9IiIpICsNCiAgZ2VvbV9zZihkYXRhPW1ldHJvX2NvdW50aWVzLA0KICAgICAgICAgIGNvbG9yID0gIndoaXRlIiwNCiAgICAgICAgICBmaWxsID0gInRyYW5zcGFyZW50IiwNCiAgICAgICAgICBsaW5ld2lkdGggPSAuNSkgKw0KICBnZW9tX3NmKGRhdGE9QXRsYW50YV9NU0EsDQogICAgICAgICAgY29sb3IgPSAiYmxhY2siLA0KICAgICAgICAgIGZpbGwgPSAidHJhbnNwYXJlbnQiLA0KICAgICAgICAgIGxpbmV3aWR0aCA9IC43NSkgKw0KICBsYWJzKHRpdGxlPSJEZXZlbG9wbWVudCBQcmVkaWN0aW9ucyAtIExvdyBUaHJlc2hvbGQiKSArIA0KICBtYXBUaGVtZSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQ0KDQpgYGANCg0KVGhlc2UgbWFwcyBzaG93IHRoYXQgdGhlIDM0JSBuaWNlbHkgY2FwdHVyZXMgdGhlIE1TQSdzIG92ZXJhbGwgcGF0dGVybiBvZiBkZXZlbG9wbWVudCB3aGlsZSBtaW5pbWl6aW5nIHRoZSB0ZW5kZW5jeSB0byBvdmVyLXByZWRpY3QgZGV2ZWxvcG1lbnQgdGhhdCB0aGUgMTclIGFuZCA1JSB0aHJlc2hvbGRzIHNoYXJlICh0aGUgNSUgdGhyZXNob2xkIHdpbGRseSBzbykuDQoNClRvIGJldHRlciB1bmRlcnN0YW5kIHRoZSB0cmFkZS1vZmZzIGJldHdlZW4gYWNjdXJhdGVseSBwcmVkaWN0aW5nIGZhbHNlIHBvc2l0aXZlcyBhbmQgZmFsc2UgbmVnYXRpdmVzLCB3ZSBjYW4gcnVuIG91ciBwcmVkaWN0aW9ucyB0aHJvdWdoIGEgY29uZnVzaW9uIG1hdHJpeCB0byB2aXN1YWxpemUgdGhlIHRydWUgcG9zaXRpdmVzICgiVHJ1ZVAiKSBhbmQgdHJ1ZSBuZWdhdGl2ZXMgKCJUcnVlTiIpIGluIHRoZSBjaGFydHMgYmVsb3csIGZvciBlYWNoIG9mIHRoZSB0aHJlc2hvbGRzLg0KDQpgYGB7ciBkZXZlbG9wbWVudCBwcmVkaWN0aW9ucyBiYXNlZCBvbiBjb25mdXNpb24gbWV0cmljcywgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTEwfQ0KZGF0IDwtIGRhdCAlPiUgDQogIG11dGF0ZShwcm9icyA9IHByZWRpY3QoTW9kZWw2LCBkYXQsIHR5cGU9InJlc3BvbnNlIikpDQogIA0KQ29uZnVzaW9uTWF0cml4Lm1ldHJpY3MgPC0NCiAgZGF0ICU+JQ0KICAgIG11dGF0ZShUaHJlc2hvbGRfNV9QY3QgPSBhcy5mYWN0b3IoaWZlbHNlKHByb2JzID49IDAuMDUgLDEsMCkpLA0KICAgICAgICAgICBUaHJlc2hvbGRfMTdfUGN0ID0gIGFzLmZhY3RvcihpZmVsc2UocHJvYnMgPj0gMC4xNyAsMSwwKSksDQogICAgICAgICAgVGhyZXNob2xkXzM0X1BjdCA9ICBhcy5mYWN0b3IoaWZlbHNlKHByb2JzID49IDAuMzQgLDEsMCkpKSAlPiUNCiAgICBtdXRhdGUoVHJ1ZVBfMDUgPSBpZmVsc2UoZGV2ZWxvcG1lbnRfY2hhbmdlICA9PSAxICYgVGhyZXNob2xkXzVfUGN0ID09IDEsIDEsMCksDQogICAgICAgICAgIFRydWVOXzA1ID0gaWZlbHNlKGRldmVsb3BtZW50X2NoYW5nZSAgPT0gMCAmIFRocmVzaG9sZF81X1BjdCA9PSAwLCAxLDApLA0KICAgICAgICAgICBUcnVlUF8xNyA9IGlmZWxzZShkZXZlbG9wbWVudF9jaGFuZ2UgID09IDEgJiBUaHJlc2hvbGRfMTdfUGN0ID09IDEsIDEsMCksDQogICAgICAgICAgIFRydWVOXzE3ID0gaWZlbHNlKGRldmVsb3BtZW50X2NoYW5nZSAgPT0gMCAmIFRocmVzaG9sZF8xN19QY3QgPT0gMCwgMSwwKSwNCiAgICAgICAgICAgVHJ1ZVBfMzQgPSBpZmVsc2UoZGV2ZWxvcG1lbnRfY2hhbmdlICA9PSAxICYgVGhyZXNob2xkXzM0X1BjdCA9PSAxLCAxLDApLA0KICAgICAgICAgICBUcnVlTl8zNCA9IGlmZWxzZShkZXZlbG9wbWVudF9jaGFuZ2UgID09IDAgJiBUaHJlc2hvbGRfMzRfUGN0ID09IDAsIDEsMCkpICU+JQ0KICAgIGRwbHlyOjpzZWxlY3QoLiwgc3RhcnRzX3dpdGgoIlRydWUiKSkgJT4lDQogICAgZ2F0aGVyKFZhcmlhYmxlLCBWYWx1ZSwgLWdlb21ldHJ5KSAlPiUNCiAgICBzdF9jYXN0KCJQT0xZR09OIikgDQoNCmdncGxvdChkYXRhPUNvbmZ1c2lvbk1hdHJpeC5tZXRyaWNzKSArDQogIGdlb21fcG9pbnQoYWVzKHg9eHlDKENvbmZ1c2lvbk1hdHJpeC5tZXRyaWNzKVssMV0sIA0KICAgICAgICAgICAgICAgICB5PXh5QyhDb25mdXNpb25NYXRyaXgubWV0cmljcylbLDJdLCBjb2xvdXIgPSBhcy5mYWN0b3IoVmFsdWUpKSkgKw0KICBmYWNldF93cmFwKH5WYXJpYWJsZSkgKw0KICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGMoZ3JheSwgaGlnaGxpZ2h0KSwNCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiSW5jb3JyZWN0IiwiQ29ycmVjdCIpLA0KICAgICAgICAgICAgICAgICAgICAgIG5hbWU9IiIpICsNCiAgZ2VvbV9zZihkYXRhPW1ldHJvX2NvdW50aWVzLA0KICAgICAgICAgIGNvbG9yID0gIndoaXRlIiwNCiAgICAgICAgICBmaWxsID0gInRyYW5zcGFyZW50IiwNCiAgICAgICAgICBsaW5ld2lkdGggPSAuNSkgKw0KICBnZW9tX3NmKGRhdGE9QXRsYW50YV9NU0EsDQogICAgICAgICAgY29sb3IgPSAiYmxhY2siLA0KICAgICAgICAgIGZpbGwgPSAidHJhbnNwYXJlbnQiLA0KICAgICAgICAgIGxpbmV3aWR0aCA9IC43NSkgKw0KICBsYWJzKHRpdGxlPSJEZXZlbG9wbWVudCBQcmVkaWN0aW9ucyAtIExvdyBUaHJlc2hvbGQiKSArDQogIG1hcFRoZW1lICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpDQoNCmBgYA0KDQpNYXBwaW5nIHRoZSBzZW5zaXRpdml0eSAodHJ1ZSBwb3NpdGl2ZXMpIGFuZCBzcGVjaWZpY2l0eSAodHJ1ZSBuZWdhdGl2ZXMpIG9mIHRoZXNlIHRocmVzaG9sZHMgc2hvd3MgaW4gZnVydGhlciBkZXRhaWwgdGhhdCB0aGUgMzQlIHRocmVzaG9sZCBwcmVkaWN0cyBub3RpY2VhYmx5IHRydWUgbmVnYXRpdmVzIHRoYW4gdGhlIG90aGVyIHRocmVzaG9sZHMgd2hpbGUgcHJlZGljdGluZyBvbmx5IHNsaWdodGx5IGxlc3MgdHJ1ZSBwb3NpdGl2ZXMuIFRoZXJlZm9yZSwgd2Ugc2VsZWN0IDM0JSBhcyB0aGUgcHJvYmFiaWxpdHkgdGhyZXNob2xkIGF0IHdoaWNoIHRvIGNsYXNzaWZ5IGxhbmQgYXMgbGlrZWx5IHRvIGJlIGRldmVsb3BlZCBpbiB0aGUgcmVtYWluZGVyIG9mIHRoaXMgcHJvamVjdC4NCg0KIyBQbGFubmluZyBTY2VuYXJpb3MNCg0KTm93IHRoYXQgd2UgaGF2ZSBkZXZlbG9wZWQgYSBwcmVkaWN0aXZlIG1vZGVsIGZvciBuZXcgZGV2ZWxvcG1lbnQsIHdlIGNhbiB1c2UgaXQgdG8gdGVzdCB0aGUgZWZmZWN0cyBvZiBwb3RlbnRpYWwgZnV0dXJlIHNjZW5hcmlvcy4gV2UgZmlyc3QgdXNlIHRoZSBtb2RlbCB0byBwcmVkaWN0IGhvdyBhIGRlbWFuZC1zaWRlIGNoYW5nZSAocHJvamVjdGVkIHBvcHVsYXRpb24pIG1pZ2h0IGFmZmVjdCBkZXZlbG9wbWVudCBpbiAyMDI5LiBXZSB0aGVuIHVzZSB0aGUgbW9kZWwgdG8gcHJlZGljdCBob3cgYSBzdXBwbHktc2lkZSBjaGFuZ2UgKGEgbmV3IGhpZ2h3YXkpIG1pZ2h0IGFmZmVjdCBkZXZlbG9wbWVudA0KDQojIyBTY2VuYXJpbyAxOiBEZW1hbmQtc2lkZSBDaGFuZ2UgRm9yZWNhc3QNCg0KSW4gdGhpcyBkZW1hbmQtc2lkZSBwcm9qZWN0aW9uLCB3ZSB3aWxsIHVzZSBwb3B1bGF0aW9uIHByb2plY3Rpb25zIGZvciB0aGUgQXRsYW50YSBNU0EgY291bnRpZXMgYW5kIGRpc3RyaWJ1dGUgdGhlIHBvcHVsYXRpb24gYW1vbmcgb3VyIGZpc2huZXQgY2VsbHMuIFRoZSBnb2FsIGlzIHRvIHZpc3VhbGl6ZSBjaGFuZ2UgYW5kIHBsYW4gZm9yIGdyb3d0aCBzY2VuYXJpb3MgYmFzZWQgb24gaG93IGFudGljaXBhdGVkIGZ1dHVyZSBwb3B1bGF0aW9uIG1pZ2h0IGRyaXZlIGRldmVsb3BtZW50Lg0KDQpGaXJzdCwgd2UgcmVwbGFjZSB0aGUgbGFnIGRldmVsb3BtZW50IDIwMDggY29sdW1uIHdpdGggdGhlIGRpc3RhbmNlIGZyb20gZGV2ZWxvcG1lbnQgaW4gMjAxOS4NCg0KYGBge3IgbGFnIGRldiAyMDE5fQ0KZGF0IDwtDQogIGRhdCAlPiUNCiAgbXV0YXRlKGxhZ0RldmVsb3BtZW50MDggPSBubl9mdW5jdGlvbih4eUMoLiksIHh5QyhmaWx0ZXIoLiwgZGV2ZWxvcGVkMTkgPT0gMSkpLDIpKQ0KYGBgDQoNCk5leHQsIHdlIGxvb2sgYXQgcHJvamVjdGVkIHBvcHVsYXRpb24gYnkgY291bnR5IGluIDIwMjkgYmFzZWQgb24gcG9wdWxhdGlvbiBlc3RpbWF0ZXMgZnJvbSB0aGUgW0dlb3JnaWEgRGF0YSBBbmFseXRpY3MgQ2VudGVyXShodHRwczovL2dkYWMuZ2VvcmdpYS5nb3YvY2Vuc3VzKS4NCg0KYGBge3IgcHJvamVjdGVkIHBvcCAyMDI5fQ0KI0NvdW50eSBwcm9qZWN0aW9ucyAyMDI5DQojQmFycm93IDk2OTc3IC0NCiNCYXJ0b3cgMTE5NTk0IC0gDQojQ2Fycm9sbCAxMzE1NzcgLQ0KI0NoZXJva2VlICAzMDE3NTItDQojQ2xheXRvbiAgMzIxNDEwLQ0KI0NvYmIgIDgzMDY3MS0NCiNDb3dldGEgIDE3MjM0MSAtDQojRGF3c29uICAgIAkzMjU3Mi0NCiNEZUthbGIgIDgyMjE1NC0NCiNEb3VnbGFzICAxNjA2MzUtDQojRmF5ZXR0ZSAgMTI4NjI3LQ0KI0ZvcnN5dGggICAzMTYxNjUtDQojRnVsdG9uICAxMTk4MzM0LQ0KI0d3aW5uZXR0ICAgMTA0NDAyNi0NCiNIYWxsICAgCTIzMjcyMC0NCiNIZW5yeSAgIDI3ODczOS0NCiNOZXd0b24gMTMwNTU5LQ0KI1BhdWxkaW5nIDIwODE5Ni0NCiNSb2NrZGFsZSAJOTU1NzQtDQojU3BhbGRpbmcgICA3MDk3NC0NCiNXYWx0b24gCTEwOTIwNS0NCg0KY291bnR5UG9wdWxhdGlvbl8yMDI5IDwtIA0KICBkYXRhLmZyYW1lKA0KICAgTkFNRSA9IENvdW50aWVzLA0KICAgY291bnR5X3Byb2plY3Rpb25fMjAyOSA9IA0KICAgICBjKDk2OTc3LDExOTU5NCwxMzE1NzcsMzAxNzUyLDMyMTQxMCw4MzA2NzEsMTcyMzQxLDMyNTcyLDgyMjE1NCwxNjA2MzUsMTI4NjI3LA0KICAgICAgIDMxNjE2NSwxMTk4MzM0LDEwNDQwMjYsMjMyNzIwLDI3ODczOSwxMzA1NTksMjA4MTk2LDk1NTc0LDcwOTc0LDEwOTIwNSkpICU+JQ0KICAgbGVmdF9qb2luKA0KICAgICBkYXQgJT4lDQogICAgICAgc3Rfc2V0X2dlb21ldHJ5KE5VTEwpICU+JQ0KICAgICAgIGdyb3VwX2J5KE5BTUUpICU+JQ0KICAgICAgIHN1bW1hcml6ZShjb3VudHlfcG9wdWxhdGlvbl8yMDE5ID0gcm91bmQoc3VtKHBvcDE5KSkpKQ0KDQpjb3VudHlQb3B1bGF0aW9uXzIwMjkgJT4lDQogIGdhdGhlcihWYXJpYWJsZSxWYWx1ZSwgLU5BTUUpICU+JQ0KICBnZ3Bsb3QoYWVzKHJlb3JkZXIoTkFNRSwtVmFsdWUpLFZhbHVlKSkgKw0KICBnZW9tX2JhcihhZXMoZmlsbD1WYXJpYWJsZSksIHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJkb2RnZSIpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTIsDQogICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCIyMDE5IiwiMjAyOSIpLA0KICAgICAgICAgICAgICAgICAgICBuYW1lPSJQb3B1bGF0aW9uIikgKw0KICBsYWJzKHRpdGxlPSJQb3B1bGF0aW9uIENoYW5nZSBieSBDb3VudHk6IDIwMTkgLSAyMDI5IiwNCiAgICAgICB4PSJDb3VudHkiLCB5PSJQb3B1bGF0aW9uIikgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKSArDQogIHBsb3RUaGVtZQ0KYGBgDQoNCldlIHNlZSB0aGF0IEZ1bHRvbiwgR3dpbm5ldHQsIERlS2FsYiwgYW5kIENvYmIgY291bnRpZXMgYXJlIHByb2plY3RlZCB0byBleHBlcmllbmNlIHRoZSBsYXJnZXN0IHBvcHVsYXRpb24gaW5jcmVhc2VzIGJldHdlZW4gMjAxOSBhbmQgMjAyOS4NCg0KIyMjIyBQcmVkaWN0aW5nIERldmVsb3BtZW50IERlbWFuZCAoYmFzZWQgb24gcHJvamVjdGVkIHBvcHVsYXRpb24pDQoNCk5vdywgd2Ugd2lsbCB0YWtlIHRoZSBjb3VudHktbGV2ZWwgcG9wdWxhdGlvbiBwcm9qZWN0aW9ucyBhbmQgZGlzdHJpYnV0ZSB0aGVtIGFjcm9zcyB0aGUgc3R1ZHkgYXJlYS4gV2UgZG8gdGhpcyBieSB3ZWlnaHRpbmcgaXQgcHJvcG9ydGlvbmFsbHkgYWNjb3JkaW5nIHRvIGEgZ3JpZCBjZWxsJ3MgMjAxOSBwb3B1bGF0aW9uIGFuZCB0aGVuIHN1YnRyYWN0aW5nIDIwMTkgcG9wIHRvIGdldCBgcG9wX2NoYW5nZWAuDQoNCmBgYHtyIHByZWRpY3RlZCBkZXZlbG9wbWVudCBkZW1hbmQgMjAyOX0NCmRhdF9pbmZpbGwgPC0NCiAgZGF0ICU+JQ0KICAjY2FsY3VsYXRlIHBvcHVsYXRpb24gY2hhbmdlDQogICAgbGVmdF9qb2luKGNvdW50eVBvcHVsYXRpb25fMjAyOSkgJT4lDQogICAgbXV0YXRlKHByb3BvcnRpb25fb2ZfY291bnR5X3BvcCA9IHBvcDE5IC8gY291bnR5X3BvcHVsYXRpb25fMjAxOSwNCiAgICAgICAgICAgcG9wXzIwMjkuaW5maWxsID0gcHJvcG9ydGlvbl9vZl9jb3VudHlfcG9wICogY291bnR5X3Byb2plY3Rpb25fMjAyOSwNCiAgICAgICAgICAgcG9wX0NoYW5nZSA9IHJvdW5kKHBvcF8yMDI5LmluZmlsbCAtIHBvcDE5KSwyKSAlPiUNCiAgICBkcGx5cjo6c2VsZWN0KC1jb3VudHlfcHJvamVjdGlvbl8yMDI5LCAtY291bnR5X3BvcHVsYXRpb25fMjAxOSwgDQogICAgICAgICAgICAgICAgICAtcHJvcG9ydGlvbl9vZl9jb3VudHlfcG9wLCAtcG9wXzIwMjkuaW5maWxsKSAlPiUNCiAgI3ByZWRpY3QgZm9yIDIwMjkNCiAgICBtdXRhdGUocHJlZGljdF8yMDI5LmluZmlsbCA9IHByZWRpY3QoTW9kZWw2LC4gLCB0eXBlPSJyZXNwb25zZSIpKQ0KDQptYXBfcHJlZDI5IDwtIA0KZGF0X2luZmlsbCAlPiUNCiAgZ2dwbG90KCkgKyAgDQogIGdlb21fcG9pbnQoYWVzKHg9eHlDKGRhdF9pbmZpbGwpWywxXSwgeT14eUMoZGF0X2luZmlsbClbLDJdLCBjb2xvdXIgPSBmYWN0b3IobnRpbGUocHJlZGljdF8yMDI5LmluZmlsbCw1KSkpKSArDQogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTUsDQogICAgICAgICAgICAgICAgICAgIGxhYmVscz1zdWJzdHIocXVpbnRpbGVCcmVha3MoZGF0X2luZmlsbCwicHJlZGljdF8yMDI5LmluZmlsbCIpLDEsNCksDQogICAgICAgICAgICAgICAgICAgIG5hbWU9IiUgTGlrZWxpaG9vZFxub2YgRGV2ZWxvcG1lbnQiKSArDQogIGdlb21fc2YoZGF0YT1tZXRyb19jb3VudGllcywgZmlsbD1OQSwgY29sb3VyPSJ3aGl0ZSIsIHNpemU9Ljc1KSArDQogIGdlb21fc2YoZGF0YSA9IEF0bGFudGFfTVNBLCBmaWxsID0gInRyYW5zcGFyZW50IiwNCiAgICAgICAgICBjb2xvciA9ICJibGFjayIsIGxpbmV3aWR0aCA9IC43NSkgKw0KICBsYWJzKHRpdGxlPSAiRGV2ZWxvcG1lbnQgRGVtYW5kIGluIDIwMjk6IFByZWRpY3RlZCBQcm9iYWJpbGl0aWVzIiwNCiAgICBzdWJ0aXRsZSA9ICJCYXNlZCBvbiBwb3B1bGF0aW9uIGNoYW5nZSBwcm9qZWN0aW9ucyIpKw0KICBtYXBUaGVtZQ0KDQptYXBfcHJlZDI5DQpgYGANCg0KTWFwcGluZyBwcmVkaWN0ZWQgZGV2ZWxvcG1lbnQgYmFzZWQgb24gcHJvamVjdGVkIHBvcHVsYXRpb24gY2hhbmdlIGZvciAyMDI5IGRvZXNuJ3Qgc2hvdyBhbnkgaHVnZSBjaGFuZ2VzIGZyb20gd2hlcmUgb3VyIG1vZGVsIHByZWRpY3RlZCBkZXZlbG9wbWVudCB3b3VsZCBiZSBpbiAyMDE5LiBOZXcgZGV2ZWxvcG1lbnQgaXMgc3RpbGwgcHJlZGljdGVkIHRvIG1vc3QgbGlrZWx5IG9jY3VyIGluIHRoZSBvdXRlciBjb3VudGllcy4gQWx0aG91Z2gsIG91ciBtb2RlbCBpcyBwcmVkaWN0aW5nIGluY3JlYXNlZCBkZXZlbG9wbWVudCBpbiB0aGUgbm9ydGh3ZXN0IGFuZCBzb3V0aHdlc3QgY29ybmVycyBvZiB0aGUgbWV0cm8gd2hpY2ggaXQgd2FzIG5vdCBwcmVkaWN0aW5nIGJlZm9yZSB3ZSBhZGRlZCBpbiB0aGUgMjAyOSBwb3B1bGF0aW9uIHByb2plY3Rpb25zLg0KDQo8YnI+DQoNCiMjIFNjZW5hcmlvIDI6IFN1cHBseS1zaWRlIENoYW5nZSBGb3JlY2FzdA0KDQpUbyBiZWdpbiBvdXIgc3VwcGx5LXNpZGUgcHJvamVjdGlvbiwgd2UgaW1hZ2luZSBhIG5ldyBoaWdod2F5IGluIFdhbHRvbiBDb3VudHksIGluIHRoZSBlYXN0ZXJuIHBhcnQgb2YgdGhlIG1ldHJvLCB3aGVyZSBkZXZlbG9wbWVudCBiZXR3ZWVuIDIwMDggYW5kIDIwMTkgd2FzIGxlc3MgY29uY2VudHJhdGVkLiBUaGlzIHdhcyBhbHNvIGEgZm9yZXN0ZWQgYXJlYSwgc28gdGhlIGZhY3QgdGhhdCBtdWNoIGRldmVsb3BtZW50IGhhZCBub3QgaGFwcGVuZWQgdGhlcmUgb3ZlciB0aGUgZWFybGllciBwZXJpb2QgYXJvdXNlZCBvdXIgY3VyaW9zaXR5LiBUaGlzIHdheSwgd2UgY2FuIHRlc3QgdGhlIGV4dGVudCB0aGF0IGEgbmV3IHJvYWQgaW52ZXN0bWVudCBpbmZsdWVuY2VzIGRldmVsb3BtZW50IGluIGEgZGVzaXJhYmxlIGFyZWEgb2YgbGFuZCBjb3Zlci4gVGhpcyBuZXcgcm9hZCB3YXMgY3JlYXRlZCBhcyBhIGxpbmUgc2VnbWVudCBpbiBBcmNHSVMgYW5kIG1lcmdlZCB3aXRoIHRoZSBleGlzdGluZyBoaWdod2F5cyBzaGFwZWZpbGUuDQoNCmBgYHtyIGltcG9ydCBuZXcgcm9hZHMgZmlsZXMsIHJlc3VsdHM9J2hpZGUnfQ0KI05FVyBISUdIV0FZIFNFR01FTlQgT05MWQ0KbmV3X2hpZ2h3YXkgPC0gc3RfcmVhZCgiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2MtdG93bnNsZXkvTFVFTV9BNV9SZXBvL2E1YTViMGM5MDk5Yjk3ZjY4YzBkMWFlYWRjZGZlOWRiZDI5NjBhOWEvRGF0YS9nZW9qc29uL25ld19oaWdod2F5Lmdlb2pzb24iKQ0KDQpuZXdfaGlnaHdheSA8LSBuZXdfaGlnaHdheSAlPiUgDQpzdF90cmFuc2Zvcm0oc3RfY3JzKG1ldHJvX2NvdW50aWVzKSkgJT4lDQogIHN0X2ludGVyc2VjdGlvbihtZXRyb19jb3VudGllcykgDQoNCiNuZXdfaGlnaHdheSA8LSBzdF93cml0ZShuZXdfaGlnaHdheSwiQzovVXNlcnMvTGluZHNleS9EZXNrdG9wL0NQTE42NzVfTGFuZF9Vc2VfTW9kZWxpbmdfRGVza3RvcC9GaW5hbC9MVUVNX0E1X1JlcG8vRGF0YS9nZW9qc29uL25ld19oaWdod2F5Lmdlb2pzb24iKQ0KDQojQUxMIEhJR0hXQVlTIE1FUkdFRCBUT0dFVEhFUiAtLSBoaWdod2F5c19hbGwgLS0gIA0KDQpoaWdod2F5c19hbGwgPC0gc3RfcmVhZCgiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2MtdG93bnNsZXkvTFVFTV9BNV9SZXBvL2E1YTViMGM5MDk5Yjk3ZjY4YzBkMWFlYWRjZGZlOWRiZDI5NjBhOWEvRGF0YS9nZW9qc29uL2hpZ2h3YXlzX2FsbC5nZW9qc29uIikgJT4lIA0Kc3RfdHJhbnNmb3JtKHN0X2NycyhtZXRyb19jb3VudGllcykpICU+JQ0KICBzdF9pbnRlcnNlY3Rpb24obWV0cm9fY291bnRpZXMpDQpgYGANCg0KYGBge3IgbWFwcGluZyB0aGUgbmV3IGhpZ2h3YXkgYWRkaXRpb24sIGZpZy53aWR0aD04fQ0KDQpnZ3Bsb3QoKSArDQogIGdlb21fcG9pbnQoZGF0YT1kZXZfY2hhbmdlX2Zpc2huZXQsIA0KICAgICAgICAgICAgIGFlcyh4PXh5QyhkZXZfY2hhbmdlX2Zpc2huZXQpJHgsIHk9eHlDKGRldl9jaGFuZ2VfZmlzaG5ldCkkeSwgY29sb3VyPWRldmVsb3BtZW50X2NoYW5nZSkpICsNCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBjKGdyYXksICIjRUQ3OTUzRkYiKSwNCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiTm8gQ2hhbmdlIiwiTmV3IERldmVsb3BtZW50IiksDQogICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICIiKSArDQogIGxhYnModGl0bGUgPSAiTGFuZCBDb3ZlciBEZXZlbG9wbWVudCBDaGFuZ2UiLA0KICAgICAgIHN1YnRpdGxlID0gIkF0bGFudGEgbWV0cm8gYXJlYSB8IEZpc2huZXQgY2VudHJvaWRzIHdpdGggZXhpc3RpbmcgaGlnaHdheXNcbmluIGRhcmsgYmx1ZSBhbmQgYSBuZXcgaGlnaHdheSBpbiBjeWFuIikgKw0KICBnZW9tX3NmKGRhdGE9bWV0cm9fY291bnRpZXMsIGZpbGw9TkEsIGNvbG91cj0id2hpdGUiLCBzaXplPS43NSkgKw0KICBnZW9tX3NmKGRhdGEgPSBBdGxhbnRhX01TQSwgZmlsbCA9ICJ0cmFuc3BhcmVudCIsDQogICAgICAgICAgY29sb3IgPSAiYmxhY2siLCBsaW5ld2lkdGggPSAuNzUpICsNCiAgZ2VvbV9zZihkYXRhPUF0bGFudGFIaWdod2F5cywgY29sb3IgPSBoaWdobGlnaHQsIGxpbmV3aWR0aCA9IC41KSArDQogIGdlb21fc2YoZGF0YT1uZXdfaGlnaHdheSwgY29sb3VyPSJjeWFuIiwgbGluZXdpZHRoPTEpICsNCiAgbWFwVGhlbWUNCg0KYGBgDQoNCk5vdywgd2l0aCB0aGUgbmV3IGhpZ2h3YXlzIGRhdGFzZXQsIHdlIGNhbiBjcmVhdGUgYSBuZXcgZGlzdGFuY2UtdG8taGlnaHdheSByYXN0ZXIsIGFuZCBmcm9tIHRoZXJlIGNvbnZlcnQgaXQgdG8gZmlzaG5ldCBjZW50cm9pZHMgd2hpY2ggd2UgbWVyZ2Ugd2l0aCB0aGUgZmlzaG5ldC4gVGhpcyBoZWxwcyB1cyB1bmRlcnN0YW5kIGhvdyBmYXIgdGhlIGZpc2huZXQgY2VsbHMgYXJlIGZyb20gdGhlIG5ldyBoaWdod2F5Lg0KDQpgYGB7ciBjcmVhdGUgaGlnaHdheSBkaXN0YW5jZSByYXN0ZXIgd2l0aCBuZXcgaGlnaHdheSBzZWdtZW50IH0NCg0KI25ld19oaWdod2F5X3Jhc3RlciA8LSANCiAjICBhcyhoaWdod2F5c19hbGwsJ1NwYXRpYWwnKSAlPiUNCiAgIyByYXN0ZXJpemUoLixlbXB0eVJhc3RlcikNCg0KI25ld19oaWdod2F5X3Jhc3Rlcl9kaXN0YW5jZSA8LSBkaXN0YW5jZShuZXdfaGlnaHdheV9yYXN0ZXIpDQojd3JpdGVSYXN0ZXIobmV3X2hpZ2h3YXlfcmFzdGVyX2Rpc3RhbmNlLCBmaWxlbmFtZT1maWxlLnBhdGgocmFzdF9kaXIsICJuZXdfaGlnaHdheV9kaXN0LnRpZiIpLCBmb3JtYXQ9IkdUaWZmIiwgb3ZlcndyaXRlPVRSVUUpDQoNCm5ld19oaWdod2F5X3Jhc3Rlcl9kaXN0YW5jZSA8LSByYXN0ZXIocGFzdGUoZGlyX2dpdCwgIjE4N2FjN2NmMjMxMjU3ZmM1MmRlMGY3ZjJhMWRjMjAxNGU0ZTZmMmQvRGF0YS9yYXN0ZXIvbmV3X2hpZ2h3YXlfZGlzdC50aWYiLCBzZXA9Jy8nKSkNCg0KbmFtZXMobmV3X2hpZ2h3YXlfcmFzdGVyX2Rpc3RhbmNlKSA8LSAiZGlzdGFuY2VfaGlnaHdheXMiDQoNCm5ld19oaWdod2F5UG9pbnRzIDwtDQogIHJhc3RlclRvUG9pbnRzKG5ld19oaWdod2F5X3Jhc3Rlcl9kaXN0YW5jZSkgJT4lDQogIGFzLmRhdGEuZnJhbWUoKSAlPiUNCiAgc3RfYXNfc2YoY29vcmRzID0gYygieCIsICJ5IiksIGNycyA9IHN0X2NycyhBdGxhbnRhTVNBX2Zpc2huZXQpKQ0KDQpuZXdfaGlnaHdheVBvaW50c19maXNobmV0IDwtIA0KICBhZ2dyZWdhdGUobmV3X2hpZ2h3YXlQb2ludHMsIEF0bGFudGFNU0FfZmlzaG5ldCwgbWVhbikgJT4lDQogIG11dGF0ZShkaXN0YW5jZV9oaWdod2F5cyA9IGlmZWxzZShpcy5uYShkaXN0YW5jZV9oaWdod2F5cyksMCxkaXN0YW5jZV9oaWdod2F5cykpDQoNCmdncGxvdCgpICsNCiAgZ2VvbV9zZihkYXRhPW1ldHJvX2NvdW50aWVzKSArDQogIGdlb21fcG9pbnQoZGF0YT1uZXdfaGlnaHdheVBvaW50c19maXNobmV0LCBhZXMoeD14eUMobmV3X2hpZ2h3YXlQb2ludHNfZmlzaG5ldClbLDFdLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHk9eHlDKG5ld19oaWdod2F5UG9pbnRzX2Zpc2huZXQpWywyXSwgDQogICAgICAgICAgICAgICAgIGNvbG91cj1mYWN0b3IobnRpbGUoZGlzdGFuY2VfaGlnaHdheXMsNSkpKSxzaXplPTEuNSkgKw0KICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGU1LA0KICAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1zdWJzdHIocXVpbnRpbGVCcmVha3MobmV3X2hpZ2h3YXlQb2ludHNfZmlzaG5ldCwiZGlzdGFuY2VfaGlnaHdheXMiKSwxLDgpLA0KICAgICAgICAgICAgICAgICAgICAgIG5hbWU9IlF1aW50aWxlXG5CcmVha3MiKSArDQogIGdlb21fc2YoZGF0YT1oaWdod2F5c19hbGwsIGNvbG91ciA9ICJibGFjayIpICsNCiAgZ2VvbV9zZihkYXRhPW5ld19oaWdod2F5LCBjb2xvdXI9ImN5YW4iLCBsaW5ld2lkdGg9Ljc1KSArDQogIGdlb21fc2YoZGF0YT1tZXRyb19jb3VudGllcywgZmlsbD1OQSwgY29sb3VyPSJ3aGl0ZSIsIHNpemU9Ljc1KSArDQogIGdlb21fc2YoZGF0YSA9IEF0bGFudGFfTVNBLCBmaWxsID0gInRyYW5zcGFyZW50IiwNCiAgICAgICAgICBjb2xvciA9ICJibGFjayIsIGxpbmV3aWR0aCA9IC43NSkgKw0KICBsYWJzKHRpdGxlID0gIkRpc3RhbmNlIHRvIEhpZ2h3YXlzIiwNCiAgICAgICBzdWJ0aXRsZSA9ICJBcyBmaXNobmV0IGNlbnRyb2lkczsgRXhpc3RpbmcgaGlnaHdheXMgdmlzdWFsaXplZCBpbiBibGFjazsgbmV3IGhpZ2h3YXkgaW4gY3lhbiIpICsNCiAgbWFwVGhlbWUNCg0KYGBgDQoNCldlIHRoZW4gdXNlIE1vZGVsIDYgdG8gcHJlZGljdCBob3cgYWRkaW5nIGEgaGlnaHdheSBtaWdodCBhZmZlY3QgZGV2ZWxvcG1lbnQgcGF0dGVybnMgaW4gMjAyOSwgYmFzZWQgb24gc2NlbmFyaW8gMS4gV2hpbGUgdGhpcyBtb2RlbCBkb2VzIG5vdCBzaG93IGEgdHJ1ZSBzdXBwbHktc2lkZSBvbmx5IGVmZmVjdCwgaXQgaXMgdXNlZnVsIGJlY2F1c2UgaXQgYWxsb3dzIHVzIHRvIGRpcmVjdGx5IGNvbXBhcmUgaG93IGFkZGluZyBpbiBhIHN1cHBseS1zaWRlIGZhY3RvciBhZmZlY3RzIHRoZSBtb2RlbCdzIGRlbWFuZC1zaWRlIG9ubHkgcHJlZGljdGlvbnMuDQoNCmBgYHtyIHByZWRpY3Qgd2l0aCBuZXcgaGlnaHdheSwgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9MTJ9DQpkYXRfaGlnaHdheSA8LQ0KICBkYXRfaW5maWxsICU+JQ0KICBkcGx5cjo6c2VsZWN0KC1kaXN0YW5jZV9oaWdod2F5cykgJT4lIA0KICBsZWZ0X2pvaW4oYXMuZGF0YS5mcmFtZShuZXdfaGlnaHdheVBvaW50c19maXNobmV0KSkgJT4lIA0KICBtdXRhdGUocHJlZGljdF8yMDI5LmRpc3RhbmNlX2hpZ2h3YXlzID0gcHJlZGljdChNb2RlbDYsLiAsIHR5cGU9InJlc3BvbnNlIikpDQoNCg0KTWFwX2hpZ2h3YXlfcHJlZCA8LQ0KZGF0X2hpZ2h3YXkgJT4lDQogIGdncGxvdCgpICsgIA0KICBnZW9tX3BvaW50KGFlcyh4PXh5QyhkYXRfaGlnaHdheSlbLDFdLCB5PXh5QyhkYXRfaGlnaHdheSlbLDJdLCBjb2xvdXIgPSBmYWN0b3IobnRpbGUocHJlZGljdF8yMDI5LmRpc3RhbmNlX2hpZ2h3YXlzLDUpKSkpICsNCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlNSwNCiAgICAgICAgICAgICAgICAgICAgbGFiZWxzPXN1YnN0cihxdWludGlsZUJyZWFrcyhkYXRfaGlnaHdheSwicHJlZGljdF8yMDI5LmRpc3RhbmNlX2hpZ2h3YXlzIiksMSw0KSwNCiAgICAgICAgICAgICAgICAgICAgbmFtZT0iJSBMaWtlbGlob29kXG5vZiBEZXZlbG9wbWVudCIpICsNCiAgZ2VvbV9zZihkYXRhPW1ldHJvX2NvdW50aWVzLCBmaWxsPU5BLCBjb2xvdXI9IndoaXRlIiwgc2l6ZT0uNzUpICsNCiAgZ2VvbV9zZihkYXRhPWhpZ2h3YXlzX2FsbCwgY29sb3VyID0gImJsYWNrIikgKw0KICBnZW9tX3NmKGRhdGE9bmV3X2hpZ2h3YXksIGNvbG91cj0iY3lhbiIsIGxpbmV3aWR0aD0uNzUpICsNCiAgZ2VvbV9zZihkYXRhID0gQXRsYW50YV9NU0EsIGZpbGwgPSAidHJhbnNwYXJlbnQiLA0KICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwgbGluZXdpZHRoID0gLjc1KSArDQogIGxhYnModGl0bGU9ICJEZXZlbG9wbWVudCBTdXBwbHkgaW4gMjAyOTogUHJlZGljdGVkIFByb2JhYmlsaXRpZXMiLA0KICAgICAgIHN1YnRpdGxlID0gIkJhc2VkIG9uIHByb2plY3RlZCBkZXZlbG9wbWVudCByZXNwb25zZSB0byBhIG5ldyBoaWdod2F5IikgKw0KICBtYXBUaGVtZQ0KDQpncmlkLmFycmFuZ2UobmNvbD0xLA0KICAgICAgICAgICAgIG1hcF9wcmVkMjksDQogICAgICAgICAgICAgTWFwX2hpZ2h3YXlfcHJlZA0KICAgICAgICAgICAgICkNCmBgYA0KDQpXaGlsZSB0aGUgY2hhbmdlIGlzIHNsaWdodCwgd2Ugc2VlIHRoYXQgYWRkaW5nIGluIHRoZSBuZXcgaGlnaHdheSBkb2VzIHNsaWdodGx5IGluY3JlYXNlIHRoZSBwcmVkaWN0ZWQgbGlrZWxpaG9vZCBvZiBkZXZlbG9wbWVudCBpbiB0aGUgYXJlYSBhcm91bmQgaXQuDQoNCiMgQWxsb2NhdGlvbg0KDQpOb3cgdGhhdCB3ZSBiZXR0ZXIgdW5kZXJzdGFuZCB0aGUgZWZmZWN0cyBvZiBkZW1hbmQtc2lkZSBmYWN0b3JzIChsaWtlIHBvcHVsYXRpb24gZ3Jvd3RoIGFuZCBwcm94aW1pdHkgdG8gZGV2ZWxvcGVkIGFyZWFzKSBhbmQgc3VwcGx5LXNpZGUgZmFjdG9ycyAobGlrZSBuZXcgaW5mcmFzdHJ1Y3R1cmUgYXZhaWxhYmxlIGxhbmQpLCB3ZSBjYW4gYW5hbHl6ZSB0aGVzZSBmYWN0b3JzIGJ5IGNvdW50eS4gVGhpcyBhbGxvd3MgdXMgdG8gYmV0dGVyIHVuZGVyc3RhbmQgd2hpY2ggY291bnRpZXMgaW4gdGhlIEF0bGFudGEgTVNBIGFyZSBiZXR0ZXIgc3VpdGVkIHRvIGRldmVsb3BtZW50IHRoYW4gb3RoZXJzLiBJdCBhbHNvIGFsbG93cyB1cyB0byBzZWUgd2hpY2ggbGFuZCB1c2UgYW5kIGRldmVsb3BtZW50IHBvbGljaWVzIG1pZ2h0IGJlIGJlc3Qgc3VpdGVkIHRvIHRoaXMgcmVnaW9uLg0KDQpMZXQncyBmaXJzdCBvcmllbnQgb3Vyc2VsdmVzIHRvIGhvdyBlYWNoIGtpbmQgb2YgbGFuZCBjb3ZlciB3YXMgZGlzdHJpYnV0ZWQgaW4gMjAxOS4NCg0KYGBge3IgYWdncmVnYXRlIDIwMTkgcmFzdGVycywgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTEwfQ0KZGF0MiA8LQ0KICBhZ2dyZWdhdGVSYXN0ZXIodGhlUmFzdGVyTGlzdDE5LCBkYXQpICU+JQ0KICBkcGx5cjo6c2VsZWN0KGRldmVsb3BlZDE5LCBmb3Jlc3QxOSwgZmFybTE5LCB3ZXRsYW5kczE5LCBvdGhlclVuZGV2ZWxvcGVkMTksIHdhdGVyMTkpICU+JQ0KICBzdF9zZXRfZ2VvbWV0cnkoTlVMTCkgJT4lDQogIGJpbmRfY29scyguLGRhdCkgJT4lDQogIHN0X3NmKCkgJT4lDQogIHN0X2Nhc3QoIlBPTFlHT04iKQ0KDQpyYXN0ZXJzMTlfbWFwIDwtIGFnZ3JlZ2F0ZWRSYXN0ZXJzMTkgJT4lDQogIGdhdGhlcih2YXIsdmFsdWUsZGV2ZWxvcGVkMTk6d2F0ZXIxOSkgJT4lDQogIHN0X2Nhc3QoIlBPTFlHT04iKSAlPiUgICAgI2p1c3QgdG8gbWFrZSBzdXJlIG5vIHdlaXJkIGdlb21ldHJpZXMgc2xpcHBlZCBpbg0KICBtdXRhdGUoWCA9IHh5QyguKSR4LA0KICAgICAgICAgWSA9IHh5QyguKSR5KSAlPiUNCiAgZ2dwbG90KCkgKw0KICAgIGdlb21fc2YoZGF0YT1tZXRyb19jb3VudGllcykgKw0KICAgIGdlb21fcG9pbnQoYWVzKFgsWSwgY29sb3VyPWFzLmZhY3Rvcih2YWx1ZSkpKSArDQogICAgZmFjZXRfd3JhcCh+dmFyKSArDQogICAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBjKGdyYXksIGhpZ2hsaWdodCksDQogICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiRmFsc2UiLCJUcnVlIiksDQogICAgICAgICAgICAgICAgICAgICAgICBuYW1lID0gIiIpICsNCiAgICBnZW9tX3NmKGRhdGE9bWV0cm9fY291bnRpZXMsDQogICAgICAgICAgY29sb3IgPSAid2hpdGUiLA0KICAgICAgICAgIGZpbGwgPSAidHJhbnNwYXJlbnQiLA0KICAgICAgICAgIGxpbmV3aWR0aCA9IC4yNSkgKw0KICAgIGdlb21fc2YoZGF0YT1BdGxhbnRhX01TQSwNCiAgICAgICAgICBjb2xvciA9ICJibGFjayIsDQogICAgICAgICAgZmlsbCA9ICJ0cmFuc3BhcmVudCIsDQogICAgICAgICAgbGluZXdpZHRoID0gLjUpICsNCiAgICBsYWJzKHRpdGxlID0gIkxhbmQgQ292ZXIgVHlwZXMsIDIwMTkiLA0KICAgICAgICAgc3VidGl0bGUgPSAiQXMgZmlzaG5ldCBjZW50cm9pZHMiKSArDQogICBtYXBUaGVtZSArDQogICB0aGVtZSgpICArDQogICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQoNCnJhc3RlcnMxOV9tYXANCmBgYA0KDQpXZSBzZWUgdGhhdCBtdWNoIG9mIHRoZSBtZXRybyBhcmVhLCBpbmNsdWRpbmcgZGV2ZWxvcGVkIGFyZWFzLCBpcyBmb3Jlc3RlZCAtIG1lYW5pbmcgaXQgaGFzIGEgbGFyZ2UgYW1vdW50IG9mIHRyZWUgY292ZXIuIEZhcm1zIGFuZCBvdGhlciB1bmRldmVsb3BlZCBsYW5kcyB0ZW5kIHRvIGJlIGxvY2F0ZWQgbW9yZSBvbiB0aGUgb3V0ZXIgZWRnZXMuIFdldGxhbmRzIG9uIHRoZSBvdGhlciBoYW5kIGFyZSBjb25jZW50cmF0ZWQgbW9zdGx5IGluIHRoZSBtZXRybyByZWdpb24ncyBzb3V0aGVybiBjb3VudGllcywgYWxvbmcgd2l0aCBhIG51bWJlciBvZiBzbWFsbCBwb25kcyBhbmQgcml2ZXJzLiBUaGUgbGFyZ2VzdCB3YXRlciBib2RpZXMsIGhvd2V2ZXIsIGFyZSBsb2NhdGVkIGluIHRoZSBub3J0aCBvZiB0aGUgbWV0cm8uDQoNCk1hcHBpbmcgdGhlc2UgbGFuZCBjb3ZlciB0eXBlcyBzZXBhcmF0ZWx5IGFsbG93cyB1cyB0byBzZWUgdGhhdCBzb21lIGNvdW50aWVzIG1heSBiZSBiZXR0ZXIgc3VpdGVkIHRvIGRldmVsb3BtZW50LCB3aGlsZSBvdGhlcnMgaGF2ZSBtb3JlIHNlbnNpdGl2ZSBsYW5kIGNvdmVyIHRoYXQgc2hvdWxkIGJlIHByb3RlY3RlZC4NCg0KSG93ZXZlciwgdGhpcyBtZXRob2QgZG9lcyBub3QgYWxsb3cgdXMgdG8gcGlucG9pbnQgd2hlcmUgdGhlc2UgbG9jYXRpb25zIGFyZSB3aXRoIG11Y2ggc3BlY2lmaWNpdHkuDQoNCiMjIFNlbnNpdGl2ZSBMYW5kIENvdmVyIExvc3QNCg0KVG8gYmV0dGVyIHVuZGVyc3RhbmQgd2hlcmUgZWNvbG9naWNhbGx5IGltcG9ydGFudCBsYW5kcyBhcmUgYmVpbmcgbG9zdCBmb3IgZGV2ZWxvcG1lbnQsIHdlIGNyZWF0ZSBhbiBpbmRpY2F0b3IgbGF5ZXIgY2FsbGVkIGBzZW5zaXRpdmVfbG9zdDE5YC4gVGhpcyBuZXcgbGF5ZXIgc2hvd3Mgd2hlcmUgZm9yZXN0IG9yIHdldGxhbmRzIHdlcmUgbG9zdCB0byBkZXZlbG9wbWVudCBiZXR3ZWVuIDIwMDkgYW5kIDIwMTkuDQoNCmBgYHtyIHNlbnNpdGl2ZSBsYW5kY292ZXIgbG9zcywgZmlnLndpZHRoPTh9DQpkYXQyIDwtDQogIGRhdDIgJT4lDQogICBtdXRhdGUoc2Vuc2l0aXZlX2xvc3QxOSA9IGlmZWxzZShmb3Jlc3QwOCA9PSAxICYgZm9yZXN0MTkgPT0gMCB8DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB3ZXRsYW5kczA4ID09IDEgJiB3ZXRsYW5kczE5ID09IDAsMSwwKSkgJT4lIA0KICBzdF90cmFuc2Zvcm0oIkVTUkk6MTAyMjY3IikNCiAgICAgICAgICAgICAgICAgICAgICANCmdncGxvdCgpICsNCiAgZ2VvbV9wb2ludChkYXRhPWRhdDIsIGFlcyh4PXh5QyhkYXQyKVssMV0sIHk9eHlDKGRhdDIpWywyXSwgY29sb3VyPWFzLmZhY3RvcihzZW5zaXRpdmVfbG9zdDE5KSkpICsNCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBjKGdyYXksIGhpZ2hsaWdodCksDQogICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoIk5vIENoYW5nZSIsIlNlbnNpdGl2ZSBMb3N0IiksDQogICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICIiKSArDQogIGdlb21fc2YoZGF0YSA9IG1ldHJvX2NvdW50aWVzLCBmaWxsID0gInRyYW5zcGFyZW50IiwgY29sb3IgPSAid2hpdGUiLCBsaW5ld2lkdGggPSAuNSkgKw0KICBnZW9tX3NmKGRhdGEgPSBBdGxhbnRhX01TQSwgZmlsbCA9ICJ0cmFuc3BhcmVudCIsIGNvbG9yID0gImJsYWNrIiwgbGluZXdpZHRoID0gLjc1KSArDQogIGxhYnModGl0bGUgPSAiU2Vuc2l0aXZlIGxhbmRzIGxvc3Q6IDIwMDkgLSAyMDE5IiwNCiAgICAgICBzdWJ0aXRsZSA9ICJBcyBmaXNobmV0IGNlbnRyb2lkcyIpICsNCiAgbWFwVGhlbWUNCmBgYA0KDQpJbnRlcmVzdGluZ2x5LCB3ZSBzZWUgdGhhdCBtb3JlIHNlbnNpdGl2ZSBhcmVhcyB3ZXJlIGxvc3QgdG8gZGV2ZWxvcG1lbnQgbmVhciB0aGUgbWV0cm8ncyBjZW50ZXIgZHVyaW5nIHRoaXMgcGVyaW9kLg0KDQojIyBMYW5kc2NhcGUgRnJhZ21lbnRhdGlvbg0KDQpUbyBjb250ZXh0dWFsaXplIHRoZXNlIHNlbnNpdGl2ZSBsb2NhdGlvbnMsIHdlIGdyb3VwIGFyZWFzIHdoZXJlIHRoZSBgd2V0bGFuZHMxOWAgYW5kIGBmb3Jlc3QxMWAgcmFzdGVycyBhcmUgY29udGlndW91cyB0byBjcmVhdGUgYSBgc2Vuc2l0aXZlX3JlZ2lvbnNgIGxheWVyIGNvbnRhaW5pbmcgYWxsIHNlbnNpdGl2ZSByZWdpb25zIHdpdGggYW4gYXJlYSBncmVhdGVyIHRoYW4gMSBhY3JlLg0KDQpgYGB7ciBzZW5zaXRpdmUgcmVnaW9ucywgZmlnLndpZHRoPTh9DQpzZW5zaXRpdmVSZWdpb25zIDwtIA0KICByYXN0ZXI6OmNsdW1wKHdldGxhbmRzMTkgKyBmb3Jlc3QxOSkgJT4lICN1bml0cyBhcmUgbWV0ZXJzDQogIHJhc3RlclRvUG9seWdvbnMoKSAlPiUNCiAgc3RfYXNfc2YoKSAlPiUNCiAgZ3JvdXBfYnkoY2x1bXBzKSAlPiUgDQogIHN1bW1hcml6ZSgpICU+JQ0KICAgIG11dGF0ZShBY3JlcyA9IGFzLm51bWVyaWMoc3RfYXJlYSguKSAqIDAuMDAwMjQ3MTEpKSAlPiUgI1RoZXJlIGFyZSAwLjAwMDI0NzExIGFjcmVzIHBlciBzcXVhcmUgbWV0ZXIgYW5kIDQwNDYuODYgc3F1YXJlIG1ldGVycyBpbiBhbiBhY3JlDQogICAgZmlsdGVyKEFjcmVzID4gNDA0Ni44NikgICU+JQ0KICBkcGx5cjo6c2VsZWN0KCkgJT4lDQogIHJhc3Rlcjo6cmFzdGVyaXplKC4sZW1wdHlSYXN0ZXIpIA0KDQpzZW5zaXRpdmVSZWdpb25zW3NlbnNpdGl2ZVJlZ2lvbnMgPiAwXSA8LSAxICANCm5hbWVzKHNlbnNpdGl2ZVJlZ2lvbnMpIDwtICJzZW5zaXRpdmVSZWdpb25zIg0KDQpkYXQyIDwtDQogIGFnZ3JlZ2F0ZVJhc3RlcihjKHNlbnNpdGl2ZVJlZ2lvbnMpLCBkYXQyKSAlPiUNCiAgZHBseXI6OnNlbGVjdChzZW5zaXRpdmVSZWdpb25zKSAlPiUNCiAgc3Rfc2V0X2dlb21ldHJ5KE5VTEwpICU+JQ0KICBiaW5kX2NvbHMoLixkYXQyKSAlPiUNCiAgc3Rfc2YoKQ0KDQpnZ3Bsb3QoKSArDQogIGdlb21fcG9pbnQoZGF0YT1kYXQyLCBhZXMoeD14eUMoZGF0MilbLDFdLCB5PXh5QyhkYXQyKVssMl0sIGNvbG91cj1hcy5mYWN0b3Ioc2Vuc2l0aXZlUmVnaW9ucykpKSArDQogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gYyhncmF5LCBoaWdobGlnaHQpLA0KICAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJPdGhlciIsIlNlbnNpdGl2ZSBSZWdpb25zIiksDQogICAgICAgICAgICAgICAgICAgICAgbmFtZT0iIikgKw0KICBnZW9tX3NmKGRhdGEgPSBtZXRyb19jb3VudGllcywgZmlsbCA9ICJ0cmFuc3BhcmVudCIsIGNvbG9yID0gIndoaXRlIiwgbGluZXdpZHRoID0gLjUpICsNCiAgZ2VvbV9zZihkYXRhID0gQXRsYW50YV9NU0EsIGZpbGwgPSAidHJhbnNwYXJlbnQiLCBjb2xvciA9ICJibGFjayIsIGxpbmV3aWR0aCA9IC43NSkgKw0KICBsYWJzKHRpdGxlID0gIlNlbnNpdGl2ZSByZWdpb25zIiwNCiAgICAgICBzdWJ0aXRsZSA9ICJDb250aW5vdXMgYXJlYXMgb2YgZWl0aGVyIHdldGxhbmRzIG9yIGZvcmVzdHNcbmdyZWF0ZXIgdGhhbiAxIGFjcmUiKSArDQogIG1hcFRoZW1lDQpgYGANCg0KVGhpcyBnaXZlcyB1cyBhIG11Y2ggbW9yZSBjbGVhciB1bmRlcnN0YW5kaW5nIG9mIHdoaWNoIGFyZWFzIG1pZ2h0IGJlIG1vc3Qgc2Vuc2l0aXZlIHRvIGRldmVsb3BtZW50IHRoYW4gbWFwcGluZyBlYWNoIHR5cGUgb2YgbGFuZCBjb3ZlciBzZXBhcmF0ZWx5IGRpZC4gV2Ugc2VlIHRoYXQgbW9zdCBvZiB0aGUgY291bnRpZXMgYWxvbmcgdGhlIG91dGVyIGJvcmRlciBvZiB0aGUgQXRsYW50YSBtZXRybyByZWdpb24gc3RpbGwgaGF2ZSByZWxhdGl2ZWx5IGxvdyBsYW5kc2NhcGUgZnJhZ21lbnRhdGlvbi4gVGhpcyBzdWdnZXN0cyB0aGF0IHRoZXNlIGNvdW50aWVzIHNob3VsZCBwcmlvcml0aXplIGluZmlsbCBkZXZlbG9wbWVudCBvdmVyIHN1YnVyYmFuIHNwcmF3bC4NCg0KIyMgU3VtbWFyaXplIGJ5IENvdW50eQ0KDQpUbyBiZXR0ZXIgdW5kZXJzdGFuZCBob3cgdGhlc2UgY2hhcmFjdGVyaXN0aWNzIGNvbXBhcmUgYWNyb3NzIGNvdW50aWVzLCB3ZSBjcmVhdGUgYSBtYXRyaXggb2YgZGVtYW5kIHNpZGUgYW5kIHN1aXRhYmlsaXR5IGZhY3RvcnMuDQoNClRoaXMgbWF0cml4IGFsbG93cyB1cyB0byBiZXR0ZXIgdW5kZXJzdGFuZCBlYWNoIGNvdW50eSdzIHN1aXRhYmlsaXR5IGZvciBkZXZlbG9wbWVudC4NCg0KYGBge3IgY291bnR5IHNwZWNpZmljIG1ldHJpY3N9DQpjb3VudHlfc3BlY2lmaWNfbWV0cmljcyA8LSANCiAgZGF0MiAlPiUNCiAgI3ByZWRpY3QgZGV2ZWxvcG1lbnQgZGVtYW5kIGZyb20gb3VyIG1vZGVsDQogIG11dGF0ZShEZXZlbG9wbWVudF9EZW1hbmQgPSBwcmVkaWN0KE1vZGVsNiwgZGF0MiwgdHlwZT0icmVzcG9uc2UiKSkgJT4lDQogICNnZXQgYSBjb3VudCBjb3VudCBvZiBncmlkIGNlbGxzIGJ5IGNvdW50eSB3aGljaCB3ZSBjYW4gdXNlIHRvIGNhbGN1bGF0ZSByYXRlcyBiZWxvdw0KICBsZWZ0X2pvaW4oc3Rfc2V0X2dlb21ldHJ5KGRhdCwgTlVMTCkgJT4lIGdyb3VwX2J5KE5BTUUpICU+JSBzdW1tYXJpemUoY291bnQgPSBuKCkpKSAlPiUNCiAgI2NhbGN1bGF0ZSBzdW1tYXJ5IHN0YXRpc3RpY3MgYnkgY291bnR5DQogIGdyb3VwX2J5KE5BTUUpICU+JQ0KICBzdW1tYXJpemUoVG90YWxfRmFybWxhbmQgPSBzdW0oZmFybTE5KSAvIG1heChjb3VudCksDQogICAgICAgICAgICBUb3RhbF9Gb3Jlc3QgPSBzdW0oZm9yZXN0MTkpIC8gbWF4KGNvdW50KSwNCiAgICAgICAgICAgIFRvdGFsX1dldGxhbmRzID0gc3VtKHdldGxhbmRzMTkpIC8gbWF4KGNvdW50KSwNCiAgICAgICAgICAgIFRvdGFsX1VuZGV2ZWxvcGVkID0gc3VtKG90aGVyVW5kZXZlbG9wZWQxOSkgLyBtYXgoY291bnQpLA0KICAgICAgICAgICAgU2Vuc2l0aXZlX0xhbmRfTG9zdCA9IHN1bShzZW5zaXRpdmVfbG9zdDE5KSAvIG1heChjb3VudCksDQogICAgICAgICAgICBTZW5zaXRpdmVfUmVnaW9ucyA9IHN1bShzZW5zaXRpdmVSZWdpb25zKSAvIG1heChjb3VudCksDQogICAgICAgICAgICBNZWFuX0RldmVsb3BtZW50X0RlbWFuZCA9IG1lYW4oRGV2ZWxvcG1lbnRfRGVtYW5kKSkgJT4lDQogICNnZXQgcG9wdWxhdGlvbiBkYXRhIGJ5IGNvdW50eQ0KICBsZWZ0X2pvaW4oY291bnR5UG9wdWxhdGlvbl8yMDI5ICU+JSANCiAgICAgICAgICAgIG11dGF0ZShQb3B1bGF0aW9uX0NoYW5nZSA9IGNvdW50eV9wcm9qZWN0aW9uXzIwMjkgLSBjb3VudHlfcG9wdWxhdGlvbl8yMDE5LA0KICAgICAgICAgICAgICAgICAgIFBvcHVsYXRpb25fQ2hhbmdlX1JhdGUgPSBQb3B1bGF0aW9uX0NoYW5nZSAvIGNvdW50eV9wcm9qZWN0aW9uXzIwMjkpICU+JQ0KICAgICAgICAgICAgZHBseXI6OnNlbGVjdChOQU1FLFBvcHVsYXRpb25fQ2hhbmdlX1JhdGUpKQ0KYGBgDQoNCmBgYHtyIHBsb3QgY291bnR5IHNwZWNpZmljIG1ldHJpY3MsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD0xMX0NCmNvdW50eV9zcGVjaWZpY19tZXRyaWNzICU+JQ0KICBnYXRoZXIoVmFyaWFibGUsIFZhbHVlLCAtTkFNRSwgLWdlb21ldHJ5KSAlPiUNCiAgbXV0YXRlKFZhcmlhYmxlID0gZmFjdG9yKFZhcmlhYmxlLCBsZXZlbHM9YygiUG9wdWxhdGlvbl9DaGFuZ2VfUmF0ZSIsIk1lYW5fRGV2ZWxvcG1lbnRfRGVtYW5kIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVG90YWxfRmFybWxhbmQiLCJUb3RhbF9VbmRldmVsb3BlZCIsIlRvdGFsX0ZvcmVzdCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlRvdGFsX1dldGxhbmRzIiwiU2Vuc2l0aXZlX0xhbmRfTG9zdCIsIlNlbnNpdGl2ZV9SZWdpb25zIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcmRlcmVkID0gVFJVRSkpKSAlPiUNCiAgbXV0YXRlKFBsYW5uaW5nX0Rlc2lnbmF0aW9uID0gY2FzZV93aGVuKA0KICAgIFZhcmlhYmxlID09ICJQb3B1bGF0aW9uX0NoYW5nZV9SYXRlIiB8IFZhcmlhYmxlID09ICJNZWFuX0RldmVsb3BtZW50X0RlbWFuZCIgfiAiRGVtYW5kLVNpZGUiLA0KICAgIFZhcmlhYmxlID09ICJUb3RhbF9GYXJtbGFuZCIgfCBWYXJpYWJsZSA9PSAiVG90YWxfVW5kZXZlbG9wZWQiICAgICAgICAgICAgICAgfiAiU3VpdGFibGUiLA0KICAgIFRSVUUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfiAiTm90IFN1aXRhYmxlIikpICU+JQ0KICBnZ3Bsb3QoYWVzKHg9VmFyaWFibGUsIHk9VmFsdWUsIGZpbGw9UGxhbm5pbmdfRGVzaWduYXRpb24pKSArDQogICAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiLCBwb3NpdGlvbj1wb3NpdGlvbl9kb2RnZSgpLCBjb2xvdXI9ImJsYWNrIikgKw0KICAgIGZhY2V0X3dyYXAofk5BTUUsIG5jb2w9NSkgKw0KICAgIGNvb3JkX2ZsaXAoKSArDQogICAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSguMjUsIDEsIGJ5ID0gLjI1KSkgKw0KICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDIuNSkgKyBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSA0LjUpICsNCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9YygiIzBEMDg4N0ZGIiwiI0JEMzc4NkZGIiwiI0ZEQzkyNkZGIikpICsNCiAgICBsYWJzKHRpdGxlPSAiQ291bnR5IFNwZWNpZmljIEFsbG9jYXRpb24gTWV0cmljcyIsIHN1YnRpdGxlPSAiQXMgcmF0ZXMiLCB4PSJJbmRpY2F0b3IiLCB5PSJSYXRlIikgKw0KICAgIHBsb3RUaGVtZSArIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSksIGxlZ2VuZC5wb3NpdGlvbj0iYm90dG9tIikNCmBgYA0KDQpUaGlzIGNoYXJ0IHNob3dzIHRocmVlIHR5cGVzIG9mIG1ldHJpY3M6ICANCi0gICBEZW1hbmQtc2lkZSBmYWN0b3JzICANCi0gICBGYWN0b3JzIHN1aXRhYmxlIGZvciBkZXZlbG9wbWVudCwgYW5kICANCi0gICBGYWN0b3JzIG5vdCBzdWl0YWJsZSBmb3IgZGV2ZWxvcG1lbnQgIA0KDQpUaGUgZGVtYW5kIHNpZGUgZmFjdG9ycyBhcmUgYE1lYW5fRGV2ZWxvcG1lbnRfRGVtYW5kYCBhbmQgYFBvcHVsYXRpb25fQ2hhbmdlX1JhdGVgLiBgTWVhbl9EZXZlbG9wbWVudF9EZW1hbmRgIGlzIHRoZSBtZWFuIHZhbHVlIG9mIHByZWRpY3RlZCBkZXZlbG9wbWVudCBkZW1hbmQgZm9yIGEgZ2l2ZW4gY291bnR5IGluIDIwMjkuIGBQb3B1bGF0aW9uX0NoYW5nZV9SYXRlYCBpcyB0aGUgc2hhcmUgb2YgYSBjb3VudHkncyBwb3B1bGF0aW9uIHRoYXQgaXMgcHJvamVjdGVkIHRvIG1vdmUtaW4gYmV0d2VlbiAyMDE5IGFuZCAyMDI5Lg0KDQpGYWN0b3JzIHN1aXRhYmxlIGZvciBkZXZlbG9wbWVudCBhcmUgYXJlYSBvZiBgVG90YWxfRmFybWxhbmRgIGFuZCBhcmVhIG9mIGBUb3RhbF9VbmRldmVsb3BlZGAgbGFuZCBpbiBhIGdpdmVuIGNvdW50eS4NCg0KRmFjdG9ycyBub3Qgc3VpdGFibGUgZm9yIGRldmVsb3BtZW50IGFyZSBhcmVhIG9mIGBTZW5zaXRpdmVfUmVnaW9uc2AsIGBTZW5zaXRpdmVfTGFuZF9Mb3N0YCwgYW5kIGBUb3RhbF9XZXRsYW5kc2AgaW4gYSBjb3VudHkuDQoNCkluIGRldGVybWluaW5nIG91ciBhbGxvY2F0aW9uIGNyaXRlcmlhLCB3ZSBkZXRlcm1pbmVkIGFueSBhcmVhIHdpdGggYSB3ZXRsYW5kIHdhcyBub3Qgc3VpdGFibGUgZm9yIGRldmVsb3BtZW50LiBBdHRlbmRpbmcgdG8gdGhlIGV4dGVybmFsaXRpZXMgb2YgdXJiYW4gc3ByYXdsLCB3ZSBjaG9zZSB0byBwcmVzZXJ2ZSB0aGVzZSB2aXRhbCBlY29zeXN0ZW1zLiBXaGVuIGl0IGNvbWVzIHRvIGZvcmVzdHMsIGhvd2V2ZXIsIGdpdmVuIHRoYXQgdGhlIEF0bGFudGEgTVNBIGhhcyBzdWNoIGV4dGVuc2l2ZSB0cmVlIGNvdmVyLCB3ZSBkZXRlcm1pbmVkIHRoYXQgZm9yZXN0ZWQgYXJlYXMgYXJlIHN1aXRhYmxlIGZvciBkZXZlbG9wbWVudC4NCg0KTG9va2luZyBhdCB0aGUgZGV2ZWxvcG1lbnQgc3VpdGFiaWxpdHkgaW5kaWNhdG9ycyBhY3Jvc3MgdGhlIDIxIGNvdW50aWVzIG9mIEdlb3JnaWEncyBNU0EsIHdlIHNlZSB0aGF0IGFsbCBjb3VudGllcyBoYXZlIGEgbGFyZ2UgcGVyY2VudGFnZSBvZiBmb3Jlc3RlZCBsYW5kIGFuZCB0aGVyZWZvcmUgb2Ygc2Vuc2l0aXZlIHJlZ2lvbnMuIExvb2tpbmcgYXQgdGhlIHJlc3Qgb2YgdGhlIGluZGljYXRvcnMgdG9nZXRoZXIgaG93ZXZlciwgd2UgY2FuIHNlZSB0aGF0IHNvbWUgY291bnRpZXMgYXJlIGJldHRlciBzdWl0ZWQgZm9yIGRldmVsb3BtZW50IHRoYW4gb3RoZXJzLg0KDQpGb3IgZXhhbXBsZSwgd2Ugc2VlIHRoYXQgSGFsbCBDb3VudHkgaGFzIGJvdGggYSBoaWdoIHJhdGUgb2YgZGV2ZWxvcG1lbnQgZGVtYW5kIGFuZCBvZiBwb3B1bGF0aW9uIGNoYW5nZS4gSXQgYWxzbyBoYXMgYSByZWxhdGl2ZWx5IGxhcmdlIGFtb3VudCBvZiBmYXJtbGFuZCBhbmQgYSByZWxhdGl2ZWx5IGxvdyBhbW91bnQgb2YgdG90YWwgd2V0bGFuZHMgYW5kIHNlbnNpdGl2ZSBsYW5kIGxvc3QuIFRvZ2V0aGVyLCB0aGVzZSBmYWN0b3JzIHN1Z2dlc3QgdGhhdCBIYWxsIENvdW50eSBoYXMgYW1wbGUgc3BhY2UgZm9yIGRldmVsb3BtZW50IHRoYXQgd2lsbCBub3QgaW1wYWN0IHNlbnNpdGl2ZSBhcmVhcyAob3RoZXIgdGhhbiBmb3Jlc3QpLg0KDQpUaGUgc2F0ZWxsaXRlIGltYWdlIGJlbG93IG9mIGRldmVsb3BtZW50IGluIEhhbGwgQ291bnR5IHNob3dzIHdoeSB0aGVyZSBpcyBhIGxhcmdlIGFtb3VudCBvZiBmb3Jlc3RlZCBsYW5kIGNvdmVyIGFjcm9zcyB0aGUgQXRsYW50YSBtZXRybyByZWdpb24uIFRoZXJlIHNlZW1zIHRvIGJlIGEgcmVnaW9uYWwgcHJlZmVyZW5jZSBmb3IgZGlzcGVyc2VkIGRldmVsb3BtZW50IGluIGZvcmVzdGVkIGxhbmQuDQoNCiFbXShodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vYy10b3duc2xleS9MVUVNX0E1X1JlcG8vZmZhMDgzZTdlMWE2M2IxMWUwMzNmMzg2ZWM3ZmUwMGIyZDM0MTRkMy9EYXRhL0ltYWdlcy9TYXRlbGxpdGVfSGFsbENvdW50eS5wbmcpDQoNCg0KDQpPbiB0aGUgb3RoZXIgaGFuZCwgQ2xheXRvbiBDb3VudHkgYXBwZWFycyB0byBiZSBwb29ybHkgc3VpdGVkIGZvciBkZXZlbG9wbWVudCBiZWNhdXNlIGl0IGhhcyByZWxhdGl2ZWx5IGhpZ2ggZGV2ZWxvcG1lbnQgZGVtYW5kLCBpcyBleHBlcmllbmNpbmcgcG9wdWxhdGlvbiBncm93dGgsIGhhcyBhIGxvdyBhbW91bnQgb2YgZmFybWxhbmQgYW5kIHVuZGV2ZWxvcGVkIGxhbmQsIGFuZCBoYXMgYSByZWxhdGl2ZWx5IGhpZ2ggYW1vdW50IG9mIHdldGxhbmRzLg0KDQpMZXQncyBsb29rIG1vcmUgY2xvc2VseSBhdCBIYWxsIENvdW50eSAoaW4gdGhlIE5vcnRoKSBhbmQgQ2xheXRvbiBDb3VudHkgKHRvIHRoZSBTb3V0aCkgdG8gc2VlIHdoYXQgdGhlc2UgdHJhaXRzIGxvb2sgbGlrZSBzcGF0aWFsbHkuDQoNCmBgYHtyIEhhbGwgYW5kIENsYXl0b24gQ291bnRpZXN9DQpnZ3Bsb3QoKSArDQogIGdlb21fc2YoZGF0YSA9IG1ldHJvX2NvdW50aWVzLCBmaWxsID0gZ3JheSwgY29sb3IgPSAid2hpdGUiLCBsaW5ld2lkdGggPSAuNSkgKw0KICBnZW9tX3NmKGRhdGEgPSBmaWx0ZXIobWV0cm9fY291bnRpZXMsIE5BTUU9PSJIYWxsIiB8IE5BTUU9PSJDbGF5dG9uIiksIGZpbGwgPSAiI0ZEQzkyNkZGIiwgY29sb3IgPSAid2hpdGUiLCBsaW5ld2lkdGggPSAuNSkgKw0KICBnZW9tX3NmKGRhdGEgPSBBdGxhbnRhX01TQSwgZmlsbCA9ICJ0cmFuc3BhcmVudCIsIGNvbG9yID0gImJsYWNrIiwgbGluZXdpZHRoID0gLjc1KSArDQogIGxhYnModGl0bGUgPSAiQXRsYW50YSBNU0EiLA0KICAgICAgIHN1YnRpdGxlID0gIkhhbGwgYW5kIENsYXl0b24gQ291bnRpZXMgSGlnaGxpZ2h0ZWQiKSArDQogIG1hcFRoZW1lDQpgYGANCg0KIyMgSGFsbCBDb3VudHkNCg0KRmlyc3QsIHdlIG1hcCBzdXBwbHkgYW5kIGRlbWFuZCBmYWN0b3JzIGZvciBIYWxsIENvdW50eSwgdG8gZXhhbWluZSBhbiBleGFtcGxlIG9mIGEgY291bnR5IHdlbGwgc3VpdGVkIHRvIGZ1cnRoZXIgZGV2ZWxvcG1lbnQuDQoNCmBgYHtyIGhhbGwgY291bnR5LCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9Nn0NCkhhbGxDb3VudHkgPC0NCiAgZGF0MiAlPiUNCiAgICBtdXRhdGUoRGV2ZWxvcG1lbnRfRGVtYW5kID0gcHJlZGljdChNb2RlbDYsIGRhdDIsIHR5cGU9InJlc3BvbnNlIikpICU+JQ0KICAgIGZpbHRlcihOQU1FID09ICJIYWxsIikgJT4lDQogICAgcmVuYW1lKCJkZXZlbG9wZWQxOSIgPSBkZXZlbG9wZWQxOS4uLjIpDQoNCkhhbGxDb3VudHlfbGFuZFVzZSA8LSByYmluZCgNCiAgZmlsdGVyKEhhbGxDb3VudHksIHdldGxhbmRzMTkgPT0gMSApICU+JQ0KICBkcGx5cjo6c2VsZWN0KCkgJT4lIG11dGF0ZShMYW5kX1VzZSA9ICJOb3QgU3VpdGFibGUiKSwNCiAgZmlsdGVyKEhhbGxDb3VudHksIGRldmVsb3BlZDE5ID09IDEpICU+JQ0KICBkcGx5cjo6c2VsZWN0KCkgJT4lIG11dGF0ZShMYW5kX1VzZSA9ICJEZXZlbG9wZWQiKSkNCg0KTWFwX0hhbGwxIDwtDQpnZ3Bsb3QoKSArDQogIGdlb21fc2YoZGF0YT1IYWxsQ291bnR5LCBhZXMoZmlsbD1mYWN0b3IobnRpbGUoRGV2ZWxvcG1lbnRfRGVtYW5kLDUpKSksIGNvbG91cj1OQSkgKw0KICBnZW9tX3BvaW50KGRhdGE9SGFsbENvdW50eV9sYW5kVXNlLCBhZXMoeD14eUMoSGFsbENvdW50eV9sYW5kVXNlKVssMV0sIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHk9eHlDKEhhbGxDb3VudHlfbGFuZFVzZSlbLDJdLCBjb2xvdXI9TGFuZF9Vc2UpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNoYXBlID0gMTUsIHNpemUgPSAzKSArDQogIGdlb21fc2YoZGF0YT1zdF9pbnRlcnNlY3Rpb24oQXRsYW50YUhpZ2h3YXlzLGZpbHRlcihtZXRyb19jb3VudGllcywgTkFNRT09IkhhbGwiKSksIGxpbmV3aWR0aD0xLCBjb2xvciA9IGRhcmtHcmF5KSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGU1LCBuYW1lPSJEZXZlbG9wbWVudFxuRGVtYW5kIiwNCiAgICAgICAgICAgICAgICAgICAgbGFiZWxzPXN1YnN0cihxdWludGlsZUJyZWFrcyhIYWxsQ291bnR5LCJEZXZlbG9wbWVudF9EZW1hbmQiKSwxLDUpKSArDQogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gYyhncmF5LCIjRjBGOTIxRkYiKSkgKyANCiAgZ2VvbV9zZihkYXRhID0gZmlsdGVyKG1ldHJvX2NvdW50aWVzLCBOQU1FPT0iSGFsbCIpLCBmaWxsID0gInRyYW5zcGFyZW50IiwgY29sb3IgPSAiYmxhY2siLCBsaW5ld2lkdGggPSAxKSArDQogIGxhYnModGl0bGUgPSAiRGV2ZWxvcG1lbnQgUG90ZW50aWFsLCAyMDI5OiBIYWxsIENvdW50eSIsDQogICAgICAgc3VidGl0bGUgPSAiV2l0aCBEZXZlbG9wZWQgYW5kIFVuc3VpdGFibGUgQXJlYXMgZnJvbSAyMDE5IikgKw0KICBtYXBUaGVtZSArDQogIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKG9yZGVyID0gMSksIGNvbG91ciA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDIpKQ0KDQpNYXBfSGFsbDIgPC0NCmdncGxvdCgpICsNCiAgZ2VvbV9zZihkYXRhPUhhbGxDb3VudHksIGFlcyhmaWxsPWZhY3RvcihudGlsZShwb3BfQ2hhbmdlLDUpKSksIGNvbG91cj1OQSkgKw0KICBnZW9tX3BvaW50KGRhdGE9SGFsbENvdW50eV9sYW5kVXNlLCBhZXMoeD14eUMoSGFsbENvdW50eV9sYW5kVXNlKVssMV0sIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHk9eHlDKEhhbGxDb3VudHlfbGFuZFVzZSlbLDJdLCBjb2xvdXI9TGFuZF9Vc2UpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNoYXBlID0gMTUsIHNpemUgPSAzKSArDQogIGdlb21fc2YoZGF0YT1zdF9pbnRlcnNlY3Rpb24oQXRsYW50YUhpZ2h3YXlzLCBmaWx0ZXIobWV0cm9fY291bnRpZXMsIE5BTUU9PSJIYWxsIikpLCBsaW5ld2lkdGg9MSwgY29sb3IgPSBkYXJrR3JheSkgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlNSwgbmFtZT0iUG9wdWxhdGlvblxuQ2hhbmdlIiwNCiAgICAgICAgICAgICAgICAgICAgbGFiZWxzPXN1YnN0cihxdWludGlsZUJyZWFrcyhIYWxsQ291bnR5LCJwb3BfQ2hhbmdlIiksMSw1KSkgKw0KICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGMoZ3JheSwiI0YwRjkyMUZGIikpICsgDQogIGdlb21fc2YoZGF0YSA9IGZpbHRlcihtZXRyb19jb3VudGllcywgTkFNRT09IkhhbGwiKSwgZmlsbCA9ICJ0cmFuc3BhcmVudCIsIGNvbG9yID0gImJsYWNrIiwgbGluZXdpZHRoID0gMSkgKw0KICBsYWJzKHRpdGxlID0gIlByb2plY3RlZCBQb3B1bGF0aW9uLCAyMDI5OiBIYWxsIENvdW50eSIsDQogICAgICAgc3VidGl0bGUgPSAiV2l0aCBEZXZlbG9wZWQgYW5kIFVuc3VpdGFibGUgQXJlYXMgZnJvbSAyMDE5IikgKw0KICBtYXBUaGVtZSArDQogIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKG9yZGVyID0gMSksIGNvbG91ciA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDIpKQ0KDQpncmlkLmFycmFuZ2UoDQogIE1hcF9IYWxsMSwNCiAgTWFwX0hhbGwyLA0KICBuY29sPTIpDQpgYGANCg0KTWFwcGluZyBIYWxsIENvdW50eSdzIGRldmVsb3BtZW50IHBvdGVudGlhbCBhbmQgcHJvamVjdGVkIHBvcHVsYXRpb24gd2l0aCBkZXZlbG9wZWQgbGFuZCBhbmQgd2V0bGFuZHMgKHRoZSAibm90IHN1aXRhYmxlIiBsYW5kKSBzaG93cyB1cyB0aGF0IHRoZSBDb3VudHkgc3RpbGwgaGFzIGEgc2lnbmlmaWNhbnQgYW1vdW50IG9mIGFyZWEgdGhhdCBpcyBzdWl0YWJsZSBmb3IgZGV2ZWxvcG1lbnQuIEluIHBhcnRpY3VsYXIsIHRoaXMgY291bnR5IHdvdWxkIGRvIHdlbGwgdG8gcHJpb3JpdGl6ZSBpbmZpbGwgZGV2ZWxvcG1lbnQgYmV0d2VlbiBoaWdod2F5cyBpbiBpdHMgbm9ydGh3ZXN0IGFuZCBzb3V0aGVybiBwb3J0aW9ucyB3aGVyZSB0aGVyZSBpcyBwcmVkaWN0ZWQgdG8gYmUgYm90aCBoaWdoIGRldmVsb3BtZW50IGRlbWFuZCBhbmQgc2lnbmlmaWNhbnQgcG9wdWxhdGlvbiBncm93dGguIFRoaXMgd291bGQgYWxzbyBoZWxwIHRvIHJlZHVjZSBmb3Jlc3QgZnJhZ21lbnRhdGlvbiBhbmQgcHJlc3N1cmUgb24gd2V0bGFuZHMgaW4gdGhlIGNvdW50eSdzIGVhc3QgYW5kIG5vcnRoZWFzdC4NCg0KIyMgQ2xheXRvbiBDb3VudHkNCg0KTmV4dCwgd2UgbWFwIHRoZSBzYW1lIHN1cHBseSBhbmQgZGVtYW5kIGZhY3RvcnMgZm9yIENsYXl0b24gQ291bnR5IHRvIGV4YW1pbmUgYW4gZXhhbXBsZSBvZiBhIGNvdW50eSBub3Qgd2VsbCBzdWl0ZWQgdG8gZnVydGhlciBkZXZlbG9wbWVudC4NCg0KYGBge3IgQ2xheXRvbiBDb3VudHksIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD0xMH0NCkNsYXl0b25Db3VudHkgPC0NCiAgZGF0MiAlPiUNCiAgICBtdXRhdGUoRGV2ZWxvcG1lbnRfRGVtYW5kID0gcHJlZGljdChNb2RlbDYsIGRhdDIsIHR5cGU9InJlc3BvbnNlIikpICU+JQ0KICAgIGZpbHRlcihOQU1FID09ICJDbGF5dG9uIikgJT4lDQogICAgcmVuYW1lKCJkZXZlbG9wZWQxOSIgPSBkZXZlbG9wZWQxOS4uLjIpDQoNCkNsYXl0b25Db3VudHlfbGFuZFVzZSA8LSByYmluZCgNCiAgZmlsdGVyKENsYXl0b25Db3VudHksIHdldGxhbmRzMTkgPT0gMSApICU+JQ0KICBkcGx5cjo6c2VsZWN0KCkgJT4lIG11dGF0ZShMYW5kX1VzZSA9ICJOb3QgU3VpdGFibGUiKSwNCiAgZmlsdGVyKENsYXl0b25Db3VudHksIGRldmVsb3BlZDE5ID09IDEpICU+JQ0KICBkcGx5cjo6c2VsZWN0KCkgJT4lIG11dGF0ZShMYW5kX1VzZSA9ICJEZXZlbG9wZWQiKSkNCg0KZ3JpZC5hcnJhbmdlKA0KZ2dwbG90KCkgKw0KICBnZW9tX3NmKGRhdGE9Q2xheXRvbkNvdW50eSwgYWVzKGZpbGw9ZmFjdG9yKG50aWxlKERldmVsb3BtZW50X0RlbWFuZCw1KSkpLCBjb2xvdXI9TkEpICsNCiAgZ2VvbV9wb2ludChkYXRhPUNsYXl0b25Db3VudHlfbGFuZFVzZSwgYWVzKHg9eHlDKENsYXl0b25Db3VudHlfbGFuZFVzZSlbLDFdLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5PXh5QyhDbGF5dG9uQ291bnR5X2xhbmRVc2UpWywyXSwgY29sb3VyPUxhbmRfVXNlKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaGFwZSA9IDE1LCBzaXplID0gNikgKw0KICBnZW9tX3NmKGRhdGE9c3RfaW50ZXJzZWN0aW9uKEF0bGFudGFIaWdod2F5cyxmaWx0ZXIobWV0cm9fY291bnRpZXMsIE5BTUU9PSJDbGF5dG9uIikpLCBsaW5ld2lkdGg9MSwgY29sb3IgPSBkYXJrR3JheSkgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlNSwgbmFtZT0iRGV2ZWxvcG1lbnRcbkRlbWFuZCIsDQogICAgICAgICAgICAgICAgICAgIGxhYmVscz1zdWJzdHIocXVpbnRpbGVCcmVha3MoQ2xheXRvbkNvdW50eSwiRGV2ZWxvcG1lbnRfRGVtYW5kIiksMSw1KSkgKw0KICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGMoZ3JheSwiI0YwRjkyMUZGIikpICsgDQogIGdlb21fc2YoZGF0YSA9IGZpbHRlcihtZXRyb19jb3VudGllcywgTkFNRT09IkNsYXl0b24iKSwgZmlsbCA9ICJ0cmFuc3BhcmVudCIsIGNvbG9yID0gImJsYWNrIiwgbGluZXdpZHRoID0gMSkgKw0KICBsYWJzKHRpdGxlID0gIkRldmVsb3BtZW50IFBvdGVudGlhbCwgMjAyOTogQ2xheXRvbiBDb3VudHkiLA0KICAgICAgIHN1YnRpdGxlID0gIldpdGggRGV2ZWxvcGVkIGFuZCBVbnN1aXRhYmxlIEFyZWFzIGZyb20gMjAxOSIpICsgDQogIG1hcFRoZW1lICsNCiAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQob3JkZXIgPSAxKSwgY29sb3VyID0gZ3VpZGVfbGVnZW5kKG9yZGVyID0gMikpLA0KDQpnZ3Bsb3QoKSArDQogIGdlb21fc2YoZGF0YT1DbGF5dG9uQ291bnR5LCBhZXMoZmlsbD1mYWN0b3IobnRpbGUocG9wX0NoYW5nZSw1KSkpLCBjb2xvdXI9TkEpICsNCiAgZ2VvbV9wb2ludChkYXRhPUNsYXl0b25Db3VudHlfbGFuZFVzZSwgYWVzKHg9eHlDKENsYXl0b25Db3VudHlfbGFuZFVzZSlbLDFdLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5PXh5QyhDbGF5dG9uQ291bnR5X2xhbmRVc2UpWywyXSwgY29sb3VyPUxhbmRfVXNlKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaGFwZSA9IDE1LCBzaXplID0gNikgKw0KICBnZW9tX3NmKGRhdGE9c3RfaW50ZXJzZWN0aW9uKEF0bGFudGFIaWdod2F5cywgZmlsdGVyKG1ldHJvX2NvdW50aWVzLCBOQU1FPT0iQ2xheXRvbiIpKSwgbGluZXdpZHRoPTEsIGNvbG9yID0gZGFya0dyYXkpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTUsIG5hbWU9IlBvcHVsYXRpb25cbkNoYW5nZSIsDQogICAgICAgICAgICAgICAgICAgIGxhYmVscz1zdWJzdHIocXVpbnRpbGVCcmVha3MoQ2xheXRvbkNvdW50eSwgInBvcF9DaGFuZ2UiKSwgMSwgNSkpICsNCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBjKGdyYXksIiNGMEY5MjFGRiIpKSArIA0KICBnZW9tX3NmKGRhdGEgPSBmaWx0ZXIobWV0cm9fY291bnRpZXMsIE5BTUU9PSJDbGF5dG9uIiksIGZpbGwgPSAidHJhbnNwYXJlbnQiLCBjb2xvciA9ICJibGFjayIsIGxpbmV3aWR0aCA9IDEpICsNCiAgbGFicyh0aXRsZSA9ICJQcm9qZWN0ZWQgUG9wdWxhdGlvbiwgMjAyOTogQ2xheXRvbiBDb3VudHkiLA0KICAgICAgc3VidGl0bGUgPSAiV2l0aCBEZXZlbG9wZWQgYW5kIFVuc3VpdGFibGUgQXJlYXMgZnJvbSAyMDE5IikgKyANCiAgbWFwVGhlbWUgKw0KICBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDEpLCBjb2xvdXIgPSBndWlkZV9sZWdlbmQob3JkZXIgPSAyKSksIG5jb2w9MikNCmBgYA0KDQpJbiBjb250cmFzdCB0byBIYWxsIENvdW50eSdzIHJlbGF0aXZlbHkgbGFyZ2UgYW1vdW50IG9mIGxhbmQgdGhhdCBpcyBzdWl0YWJsZSBmb3IgZGV2ZWxvcG1lbnQsIHdlIHNlZSB0aGF0IENsYXl0b24gQ291bnR5IGhhcyBhbG1vc3Qgbm9uZS4gVGhlIGNvdW50eSBpcyBhbG1vc3QgZW50aXJlbHkgZGV2ZWxvcGVkIGFuZCB0aGUgdHdvIGFyZWFzIGluIHRoZSBzb3V0aGVybiBwb3J0aW9uIG9mIHRoZSBjb3VudHkgdGhhdCBhcmUgbm90IGRldmVsb3BlZCBhcmUgZG9taW5hdGVkIGJ5IHdldGxhbmQgYXJlYXMgdGhhdCBhcmUgbm90IHN1aXRhYmxlIGZvciBkZXZlbG9wbWVudC4NCg0KRnVydGhlcm1vcmUsIHRoZSBtYXAgc2hvd3MgdGhhdCB0aGUgcmVtYWluaW5nIGNlbGxzIGFyZSBhbGwgbmVhciB3ZXRsYW5kIGFyZWFzIG9yIG5vdCBleHBlY3RlZCB0byBzZWUgcG9wdWxhdGlvbiBncm93dGguIFRoZXJlZm9yZSwgQ2xheXRvbiBDb3VudHkgaXMgYW4gaWRlYWwgbG9jYXRpb24gZm9yIGNvbnNlcnZhdGlvbiByYXRoZXIgdGhhbiBkZXZlbG9wbWVudCBpbiB0aGUgQXRsYW50YSBtZXRybyBhcmVhLg0KDQojIENvbmNsdXNpb24NCg0KVGhhbmsgeW91IGZvciB3YWxraW5nIHRocm91Z2ggb3VyIG1vZGVsLiBXZSBjb25jbHVkZSBieSBzdW1tYXJpemluZyB0aGUgc3RyZW5ndGhzLCB3ZWFrbmVzc2VzLCBhbmQgcGxhbm5pbmcgaW1wbGljYXRpb25zIG9mIHRoaXMgYXBwcm9hY2guDQoNCioqU3RyZW5ndGhzIG9mIGFwcHJvYWNoKiogIA0KV2hpbGUgbm90IHBlcmZlY3QsIHRoZSBzdHJlbmd0aHMgb2YgdGhpcyBhcHByb2FjaCBhcmUgdGhhdCBpdCBhbGxvd3MgdXMgdG8gZXN0aW1hdGUgZnV0dXJlIHVyYmFuIHNwcmF3bCBiYXNlZCBvbiBsYW5kIGNvdmVyIGFuZCBwb3B1bGF0aW9uIGNoYXJhY3RlcmlzdGljcyBvZiBhIGdpdmVuIGFyZWEuIEJlaW5nIGFibGUgdG8gdmlzdWFsbHkgYW5hbHl6ZSBkZXZlbG9wbWVudCBwb3RlbnRpYWwgdGhyb3VnaCBtYXBzIGFuZCBjaGFydHMgaGFzIGdyZWF0IHV0aWxpdHkgZm9yIGNvbW11bmljYXRpbmcgdGhlIGNvbXBsZXggbmF0dXJlIG9mIHVyYmFuIHNwcmF3bCB0byBldmVyeW9uZSBmcm9tIHBvbGljeW1ha2VycyB0byBjb21tdW5pdHkgbWVtYmVycyB3aXRoIG5vbi10ZWNobmljYWwgYmFja2dyb3VuZHMuDQoNCioqV2Vha25lc3NlcyoqICANCldoaWxlIGFuIGluY3JlZGlibHkgdXNlZnVsIHRvb2wsIGJlY2F1c2UgdGhpcyBhcHByb2FjaCBvbmx5IHRha2VzIGlucHV0cyBmcm9tIHR3byBzdHVkeSB5ZWFycywgaXQgaGFzIHRoZSBwb3RlbnRpYWwgdG8gcHJlZGljdCBmdXR1cmUgZGV2ZWxvcG1lbnQgYmFzZWQgb24gYSBzbWFsbCBoaXN0b3JpY2FsIHNhbXBsZSBhbmQgYW5vbWFsb3VzIHRyZW5kcy4gRnVydGhlcm1vcmUsIHdoaWxlIHRoZSBtYXBzIGFuZCBjaGFydHMgYXJlIGhlbHBmdWwgZm9yIGNvbW11bmljYXRpb24sIHRoZXkgYXJlIGVzdGltYXRpb25zIGF0IGJlc3QsIGFuZCBvbmx5IG9uZSBwaWVjZSBvZiB0aGUgbGFuZCBhbGxvY2F0aW9uIHRvb2xraXQuIFRoZSBjb252aW5jaW5nIG5hdHVyZSBvZiB0aGVzZSBncmFwaGljcyByaXNrcyBjb21tdW5pY2F0aW5nIHRoZXNlIGVzdGltYXRlcyBhcyBmYWN0LiBGaW5hbGx5LCB0aGUgbW9kZWwgaXMgb25seSBhcyBnb29kIGFzIHRoZSB2YXJpYWJsZXMgd2l0aGluIGl0LiBJbiBmdXR1cmUgc3R1ZGllcywgd2UgY291bGQgaW1wcm92ZSBvdXIgbW9kZWwgYnkgdGVzdGluZyBhZGRpdGlvbmFsIHByZWRpY3RvciB2YXJpYWJsZXMgc3VjaCBhcyBzY2hvb2wgZGlzdHJpY3RzLCBkZW1vZ3JhcGhpYyBpbmZvcm1hdGlvbiwgYW5kIHJlYWwgZXN0YXRlIHZhbHVlcy4NCg0KKipQb2xpY3kgaW1wbGljYXRpb25zKiogIA0KQmFzZWQgb24gdGhlIHJlc3VsdHMgb2Ygb3VyIGFuYWx5c2lzLCB0aGUgMjEgY291bnR5IEF0bGFudGEgbWV0cm8gcmVnaW9uIHdvdWxkIGJlbmVmaXQgZnJvbSBjb25zZXJ2YXRpb24gZWFzZW1lbnRzIHRvIHByZXZlbnQgZnVydGhlciBsYW5kc2NhcGUgZnJhZ21lbnRhdGlvbi4gVGhpcyB3b3VsZCBwcm90ZWN0IHRoZSB2aXRhbGl0eSBvZiBmb3Jlc3QgZWNvc3lzdGVtcyBvdmVyIHRoZSBsb25nLXRlcm0uIFRvIGNhbGlicmF0ZSBhZ2FpbnN0IGZ1cnRoZXIgbGFuZHNjYXBlIGZyYWdtZW50YXRpb24sIHRoaXMgYW5hbHlzaXMgc3VnZ2VzdHMgdGhhdCBwb2xpY3ltYWtlcnMgY291bGQgaW1wbGVtZW50IGFuIHVyYmFuIGdyb3d0aCBib3VuZGFyeSBpbiB0aGUgZnJpbmdlIGNvdW50aWVzIHRvIHByb3RlY3QgdGhlIG1ldHJvJ3MgcmVtYWluaW5nIHNlbnNpdGl2ZSByZWdpb25zIGJlZm9yZSB0aGV5IGFyZSBmdWxseSBkZXZlbG9wZWQuIFRoaXMgd291bGQgaGF2ZSB0aGUgYWRkZWQgYmVuZWZpdCBvZiBlbmNvdXJhZ2luZyBpbmZpbGwgZGV2ZWxvcG1lbnQgaW4gdGhlIE1TQS4NCg0KYGBge3IgZXhwb3J0IGNoYXJ0cywgaW5jbHVkZSA9IEZBTFNFLCBldmFsID0gRkFMU0V9DQpkaXJfY3RfZ3JhcGhpY3MgPC0gIkM6L1VzZXJzL2N0b3duL09uZURyaXZlIC0gUGVubk8zNjUvQ2xhc3Nlcy9DbGFzc2VzX1NlbTRfMjAyM1NwcmluZy9DUExOIDY3NV9MYW5kIE1vZGVsaW5nL0Fzc2lnbm1lbnRzL0E1X1NwcmF3bEZvcmVjYXN0aW5nL0dyYXBoaWNzIg0KDQpnZ3NhdmUocGFzdGUoZGlyX2N0X2dyYXBoaWNzLCAiSGFsbENvdW50eTEucGRmIiwgc2VwPScvJyksIHBsb3QgPSBNYXBfSGFsbDEsIHdpZHRoID0gMTEpDQpnZ3NhdmUocGFzdGUoZGlyX2N0X2dyYXBoaWNzLCAiSGFsbENvdW50eTIucGRmIiwgc2VwPScvJyksIHBsb3QgPSBNYXBfSGFsbDIsIHdpZHRoID0gMTEpDQoNCmdnc2F2ZShwYXN0ZShkaXJfY3RfZ3JhcGhpY3MsICJIaWdod2F5UHJlZC5wZGYiLCBzZXA9Jy8nKSwgcGxvdCA9IE1hcF9oaWdod2F5X3ByZWQsIHdpZHRoID0gMTEpDQpnZ3NhdmUocGFzdGUoZGlyX2N0X2dyYXBoaWNzLCAiUG9wUHJlZE1hcC5wZGYiLCBzZXA9Jy8nKSwgcGxvdCA9IG1hcF9wcmVkMjksIHdpZHRoID0gMTEpDQoNCmdnc2F2ZShwYXN0ZShkaXJfY3RfZ3JhcGhpY3MsICJDaGFydF9UZXN0UHJvYnMucGRmIiwgc2VwPScvJyksIHBsb3QgPSBjaGFydF90ZXN0UHJvYnMsIHdpZHRoID0gMTEpDQoNCmdnc2F2ZShwYXN0ZShkaXJfY3RfZ3JhcGhpY3MsICJMYW5kY292ZXIwOC5wZGYiLCBzZXA9Jy8nKSwgcGxvdCA9IHJhc3RlcnMwOF9tYXAsIHdpZHRoID0gMTEpDQpgYGANCg==